├── docs ├── .nvmrc ├── .npmrc ├── content │ └── docs │ │ ├── index.md │ │ ├── 1.getting-started │ │ ├── .navigation.yml │ │ ├── 8.changelog.md │ │ └── 1.index.md │ │ ├── 5.guide │ │ ├── .navigation.yml │ │ └── 1.migrating-from-official-php-images.md │ │ ├── 3.framework-guides │ │ ├── 1.laravel │ │ │ ├── .navigation.yml │ │ │ ├── 3.queue.md │ │ │ ├── 4.horizon.md │ │ │ └── 4.reverb.md │ │ └── 2.wordpress │ │ │ └── .navigation.yml │ │ ├── 6.customizing-the-image │ │ ├── .navigation.yml │ │ └── 1.changing-common-php-settings.md │ │ ├── 4.deployment-and-production │ │ ├── 99.platforms │ │ │ └── .navigation.yml │ │ └── 98.orchestrators │ │ │ └── .navigation.yml │ │ ├── 2.image-variations │ │ ├── cli.md │ │ └── unit.md │ │ └── 7.troubleshooting │ │ └── 1.common-issues.md ├── public │ ├── favicon.ico │ ├── favicon-96x96.png │ ├── apple-touch-icon.png │ ├── images │ │ ├── docs │ │ │ ├── php-info.png │ │ │ ├── watch-repo.png │ │ │ ├── docker-layers.png │ │ │ ├── container-layers.png │ │ │ ├── running-php-container.png │ │ │ ├── permissions-privileged.png │ │ │ ├── php-info-with-changes-options.png │ │ │ ├── reverse-proxy-ssl-zerodowntime.png │ │ │ └── php-info-changes-version-and-server.png │ │ ├── logos │ │ │ ├── og-logo.png │ │ │ ├── og-ssu-logo.png │ │ │ ├── x-logo.svg │ │ │ ├── nomad.svg │ │ │ ├── ruby.svg │ │ │ ├── python.svg │ │ │ ├── twitter-slate.svg │ │ │ ├── twitter-white.svg │ │ │ ├── github-white.svg │ │ │ ├── github-slate.svg │ │ │ ├── node.svg │ │ │ ├── php.svg │ │ │ ├── discord-white.svg │ │ │ ├── discord-slate.svg │ │ │ ├── amplitude.svg │ │ │ ├── go.svg │ │ │ └── docker.svg │ │ ├── social-image.jpg │ │ └── testimonials │ │ │ ├── chris-fidao.png │ │ │ ├── ziga-zajc.png │ │ │ └── johan-janssens.png │ ├── web-app-manifest-192x192.png │ ├── web-app-manifest-512x512.png │ ├── site.webmanifest │ └── favicon.svg ├── tsconfig.json ├── eslint.config.mjs ├── app │ ├── components │ │ ├── LeadP.vue │ │ ├── HeroVideo.vue │ │ ├── AppFooter.vue │ │ ├── Badges.vue │ │ ├── GetStarted.vue │ │ ├── Sponsors.vue │ │ ├── TemplateMenu.vue │ │ ├── AppHeader.vue │ │ ├── PageHeaderLinks.vue │ │ ├── Testimonials.vue │ │ ├── AppLogo.vue │ │ └── LandingSignup.vue │ ├── assets │ │ ├── icons │ │ │ ├── features │ │ │ │ ├── heartbeat-square.svg │ │ │ │ ├── search-icon.svg │ │ │ │ ├── heart.svg │ │ │ │ ├── heart-square.svg │ │ │ │ ├── community-icon.svg │ │ │ │ ├── lightning-square.svg │ │ │ │ ├── docs-icon.svg │ │ │ │ ├── rocket-square.svg │ │ │ │ ├── shield-square.svg │ │ │ │ ├── stars-square.svg │ │ │ │ ├── logging-square.svg │ │ │ │ ├── cloudflare-square.svg │ │ │ │ ├── php-square.svg │ │ │ │ └── nginx-square.svg │ │ │ ├── services │ │ │ │ ├── nginx.svg │ │ │ │ ├── docker.svg │ │ │ │ └── kubernetes.svg │ │ │ └── hosts │ │ │ │ ├── hetzner-square.svg │ │ │ │ ├── digitalocean-square.svg │ │ │ │ ├── sevalla-square.svg │ │ │ │ ├── vultr-square.svg │ │ │ │ └── hetzner.svg │ │ └── css │ │ │ └── main.css │ ├── pages │ │ ├── index.vue │ │ └── [...slug].vue │ ├── error.vue │ ├── app.vue │ └── layouts │ │ └── docs.vue ├── renovate.json ├── .env.example ├── .gitignore ├── content.config.ts ├── server │ └── routes │ │ └── raw │ │ └── [...slug].md.get.ts ├── package.json ├── README.md └── modules │ └── pre-render-raw-routes.ts ├── .dockerignore ├── .gitattributes ├── src ├── s6 │ ├── etc │ │ └── s6-overlay │ │ │ └── s6-rc.d │ │ │ ├── php-fpm │ │ │ ├── dependencies │ │ │ ├── type │ │ │ ├── down-signal │ │ │ ├── notification-fd │ │ │ ├── data │ │ │ │ └── check │ │ │ └── run │ │ │ └── user │ │ │ └── contents.d │ │ │ └── php-fpm │ └── usr │ │ └── local │ │ └── bin │ │ ├── docker-php-serversideup-s6-install │ │ └── docker-php-serversideup-s6-init ├── variations │ ├── fpm-apache │ │ └── etc │ │ │ ├── s6-overlay │ │ │ └── s6-rc.d │ │ │ │ ├── apache2 │ │ │ │ ├── type │ │ │ │ ├── notification-fd │ │ │ │ ├── dependencies │ │ │ │ ├── run │ │ │ │ └── data │ │ │ │ │ └── check │ │ │ │ └── user │ │ │ │ └── contents.d │ │ │ │ └── apache2 │ │ │ └── apache2 │ │ │ ├── sites-available │ │ │ ├── ssl-off.conf │ │ │ ├── ssl-mixed.conf │ │ │ └── ssl-full.conf │ │ │ ├── ports.conf │ │ │ ├── mods-available │ │ │ └── mpm_event.conf │ │ │ ├── conf-available │ │ │ ├── remoteip.conf │ │ │ ├── serversideup.conf │ │ │ └── security.conf │ │ │ └── vhost-templates │ │ │ ├── http.conf │ │ │ └── https.conf │ ├── fpm-nginx │ │ └── etc │ │ │ ├── s6-overlay │ │ │ └── s6-rc.d │ │ │ │ ├── nginx │ │ │ │ ├── type │ │ │ │ ├── dependencies │ │ │ │ ├── down-signal │ │ │ │ ├── notification-fd │ │ │ │ ├── run │ │ │ │ └── data │ │ │ │ │ └── check │ │ │ │ └── user │ │ │ │ └── contents.d │ │ │ │ └── nginx │ │ │ └── nginx │ │ │ ├── sites-available │ │ │ ├── ssl-off │ │ │ ├── ssl-mixed │ │ │ └── ssl-full.template │ │ │ ├── server-opts.d │ │ │ ├── security.conf │ │ │ ├── remoteip.conf │ │ │ └── performance.conf │ │ │ ├── nginx.conf.template │ │ │ └── site-opts.d │ │ │ ├── http.conf.template │ │ │ └── https.conf.template │ ├── frankenphp │ │ └── etc │ │ │ └── frankenphp │ │ │ ├── auto-https │ │ │ ├── off.caddyfile │ │ │ ├── disable_certs.caddyfile │ │ │ ├── disable_redirects.caddyfile │ │ │ ├── ignore_loaded_certs.caddyfile │ │ │ └── on.caddyfile │ │ │ ├── ssl-mode │ │ │ ├── off.caddyfile │ │ │ ├── mixed.caddyfile │ │ │ └── full.caddyfile │ │ │ └── log-level │ │ │ ├── address │ │ │ ├── alert.caddyfile │ │ │ ├── crit.caddyfile │ │ │ ├── debug.caddyfile │ │ │ ├── emerg.caddyfile │ │ │ ├── error.caddyfile │ │ │ ├── info.caddyfile │ │ │ ├── notice.caddyfile │ │ │ └── warn.caddyfile │ │ │ └── global │ │ │ ├── alert.caddyfile │ │ │ ├── crit.caddyfile │ │ │ ├── emerg.caddyfile │ │ │ ├── error.caddyfile │ │ │ ├── info.caddyfile │ │ │ ├── notice.caddyfile │ │ │ ├── warn.caddyfile │ │ │ └── debug.caddyfile │ └── cli │ │ └── Dockerfile ├── common │ ├── usr │ │ └── local │ │ │ ├── bin │ │ │ ├── healthcheck-horizon │ │ │ ├── healthcheck-octane │ │ │ ├── healthcheck-queue │ │ │ ├── healthcheck-reverb │ │ │ ├── healthcheck-schedule │ │ │ ├── artisan │ │ │ ├── docker-php-serversideup-install-php-ext-installer │ │ │ ├── docker-php-serversideup-dep-install-alpine │ │ │ ├── docker-php-serversideup-dep-install-debian │ │ │ ├── docker-php-serversideup-entrypoint │ │ │ └── docker-php-serversideup-set-id │ │ │ └── etc │ │ │ └── php │ │ │ └── conf.d │ │ │ └── zzz-serversideup-docker-php-debug.ini │ └── etc │ │ └── entrypoint.d │ │ ├── 0-container-info.sh │ │ └── 1-log-output-level.sh ├── php-fpm.d │ ├── usr │ │ └── local │ │ │ └── etc │ │ │ └── php-fpm.d │ │ │ └── zzz-docker-php-serversideup-fpm-debug.conf │ └── etc │ │ └── entrypoint.d │ │ └── 5-fpm-pool-user.sh └── utilities-webservers │ └── etc │ └── entrypoint.d │ └── 5-generate-ssl.sh ├── .github ├── img │ └── header.png ├── workflows │ ├── action_publish-images-dev-main.yml │ ├── action_update-dockerhub-readme.yml │ ├── action_publish-images-production.yml │ ├── action_publish-images-beta.yml │ ├── action_publish-images-prs.yml │ └── scheduled-task_update-sponsors.yml └── ISSUE_TEMPLATE │ ├── config.yml │ └── bug.yml ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── .gitignore ├── SECURITY.md ├── AGENTS.md └── scripts ├── generate-matrix.sh └── conf └── php-versions-base-config.yml /docs/.nvmrc: -------------------------------------------------------------------------------- 1 | 22 -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /docs/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | -------------------------------------------------------------------------------- /src/s6/etc/s6-overlay/s6-rc.d/php-fpm/dependencies: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/s6/etc/s6-overlay/s6-rc.d/php-fpm/type: -------------------------------------------------------------------------------- 1 | longrun -------------------------------------------------------------------------------- /src/s6/etc/s6-overlay/s6-rc.d/user/contents.d/php-fpm: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/s6/etc/s6-overlay/s6-rc.d/php-fpm/down-signal: -------------------------------------------------------------------------------- 1 | SIGQUIT -------------------------------------------------------------------------------- /src/s6/etc/s6-overlay/s6-rc.d/php-fpm/notification-fd: -------------------------------------------------------------------------------- 1 | 3 -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/s6-overlay/s6-rc.d/apache2/type: -------------------------------------------------------------------------------- 1 | longrun -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/type: -------------------------------------------------------------------------------- 1 | longrun -------------------------------------------------------------------------------- /docs/content/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | redirect: /docs/getting-started 3 | --- -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/s6-overlay/s6-rc.d/apache2/notification-fd: -------------------------------------------------------------------------------- 1 | 3 -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/s6-overlay/s6-rc.d/user/contents.d/apache2: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/dependencies: -------------------------------------------------------------------------------- 1 | php-fpm -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/down-signal: -------------------------------------------------------------------------------- 1 | SIGQUIT -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/notification-fd: -------------------------------------------------------------------------------- 1 | 3 -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/user/contents.d/nginx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/s6-overlay/s6-rc.d/apache2/dependencies: -------------------------------------------------------------------------------- 1 | php-fpm -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/auto-https/off.caddyfile: -------------------------------------------------------------------------------- 1 | auto_https off 2 | -------------------------------------------------------------------------------- /docs/content/docs/1.getting-started/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: Getting Started 2 | icon: false 3 | -------------------------------------------------------------------------------- /src/s6/etc/s6-overlay/s6-rc.d/php-fpm/data/check: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv sh 2 | php-fpm-healthcheck -------------------------------------------------------------------------------- /.github/img/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/.github/img/header.png -------------------------------------------------------------------------------- /docs/content/docs/5.guide/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: Advanced Guides 2 | icon: false 3 | defaultOpen: false -------------------------------------------------------------------------------- /src/common/usr/local/bin/healthcheck-horizon: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | php "${APP_BASE_DIR}/artisan" horizon:status -------------------------------------------------------------------------------- /src/common/usr/local/bin/healthcheck-octane: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | php "${APP_BASE_DIR}/artisan" octane:status -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/favicon.ico -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/auto-https/disable_certs.caddyfile: -------------------------------------------------------------------------------- 1 | auto_https disable_certs 2 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/auto-https/disable_redirects.caddyfile: -------------------------------------------------------------------------------- 1 | auto_https disable_redirects 2 | -------------------------------------------------------------------------------- /docs/content/docs/3.framework-guides/1.laravel/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: Laravel 2 | icon: false 3 | defaultOpen: false -------------------------------------------------------------------------------- /docs/content/docs/6.customizing-the-image/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: Customization 2 | icon: false 3 | defaultOpen: false -------------------------------------------------------------------------------- /docs/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/favicon-96x96.png -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/auto-https/ignore_loaded_certs.caddyfile: -------------------------------------------------------------------------------- 1 | auto_https ignore_loaded_certs 2 | -------------------------------------------------------------------------------- /docs/content/docs/3.framework-guides/2.wordpress/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: WordPress 2 | icon: false 3 | defaultOpen: false -------------------------------------------------------------------------------- /docs/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/run: -------------------------------------------------------------------------------- 1 | #!/command/execlineb -P 2 | with-contenv 3 | s6-notifyoncheck 4 | nginx -------------------------------------------------------------------------------- /docs/content/docs/4.deployment-and-production/99.platforms/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: Platforms 2 | icon: false 3 | defaultOpen: false -------------------------------------------------------------------------------- /docs/public/images/docs/php-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/php-info.png -------------------------------------------------------------------------------- /docs/public/images/logos/og-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/logos/og-logo.png -------------------------------------------------------------------------------- /docs/public/images/social-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/social-image.jpg -------------------------------------------------------------------------------- /docs/content/docs/4.deployment-and-production/98.orchestrators/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: Orchestrators 2 | icon: false 3 | defaultOpen: false -------------------------------------------------------------------------------- /docs/public/images/docs/watch-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/watch-repo.png -------------------------------------------------------------------------------- /docs/public/images/docs/docker-layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/docker-layers.png -------------------------------------------------------------------------------- /docs/public/images/logos/og-ssu-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/logos/og-ssu-logo.png -------------------------------------------------------------------------------- /docs/public/web-app-manifest-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/web-app-manifest-192x192.png -------------------------------------------------------------------------------- /docs/public/web-app-manifest-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/web-app-manifest-512x512.png -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/ssl-mode/off.caddyfile: -------------------------------------------------------------------------------- 1 | {$CADDY_HTTP_SERVER_ADDRESS:http://} { 2 | import php-app-common 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/.nuxt": true, 4 | "**/node_modules": true, 5 | "**/dist": true 6 | } 7 | } -------------------------------------------------------------------------------- /docs/public/images/docs/container-layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/container-layers.png -------------------------------------------------------------------------------- /src/s6/etc/s6-overlay/s6-rc.d/php-fpm/run: -------------------------------------------------------------------------------- 1 | #!/command/execlineb -P 2 | with-contenv 3 | s6-notifyoncheck -d 4 | /usr/local/sbin/php-fpm --nodaemonize -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/auto-https/on.caddyfile: -------------------------------------------------------------------------------- 1 | # Auto HTTPS is enabled by default when no auto_https directive is specified 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | See our Contribution Guide: https://serversideup.net/open-source/docker-php/docs/getting-started/contributing -------------------------------------------------------------------------------- /docs/public/images/testimonials/chris-fidao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/testimonials/chris-fidao.png -------------------------------------------------------------------------------- /docs/public/images/testimonials/ziga-zajc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/testimonials/ziga-zajc.png -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/sites-available/ssl-off: -------------------------------------------------------------------------------- 1 | # HTTP configuration 2 | # 3 | server { 4 | include /etc/nginx/site-opts.d/http.conf; 5 | } -------------------------------------------------------------------------------- /docs/public/images/docs/running-php-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/running-php-container.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ensure node files do not appear 2 | package-lock.json 3 | package.json 4 | yarn.lock 5 | node_modules 6 | php-versions.yml 7 | *.tmp 8 | /docs/_OLD_ -------------------------------------------------------------------------------- /docs/public/images/docs/permissions-privileged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/permissions-privileged.png -------------------------------------------------------------------------------- /docs/public/images/testimonials/johan-janssens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/testimonials/johan-janssens.png -------------------------------------------------------------------------------- /docs/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import withNuxt from './.nuxt/eslint.config.mjs' 3 | 4 | export default withNuxt( 5 | // Your custom configs here 6 | ) 7 | -------------------------------------------------------------------------------- /docs/public/images/docs/php-info-with-changes-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/php-info-with-changes-options.png -------------------------------------------------------------------------------- /docs/public/images/docs/reverse-proxy-ssl-zerodowntime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/reverse-proxy-ssl-zerodowntime.png -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/sites-available/ssl-off.conf: -------------------------------------------------------------------------------- 1 | 2 | Include /etc/apache2/vhost-templates/http.conf 3 | -------------------------------------------------------------------------------- /docs/public/images/docs/php-info-changes-version-and-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/docker-php/HEAD/docs/public/images/docs/php-info-changes-version-and-server.png -------------------------------------------------------------------------------- /docs/app/components/LeadP.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/alert.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/crit.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/debug.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:INFO} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/emerg.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/error.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/info.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:INFO} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/notice.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:INFO} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/address/warn.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:WARN} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/alert.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/crit.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/emerg.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/error.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:ERROR} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/info.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:INFO} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/notice.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:INFO} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/warn.caddyfile: -------------------------------------------------------------------------------- 1 | log { 2 | format {$CADDY_LOG_FORMAT:console} 3 | output {$CADDY_LOG_OUTPUT:stdout} 4 | level {$CADDY_SERVER_LOG_LEVEL:WARN} 5 | } 6 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/log-level/global/debug.caddyfile: -------------------------------------------------------------------------------- 1 | debug 2 | log { 3 | format {$CADDY_LOG_FORMAT:console} 4 | output {$CADDY_LOG_OUTPUT:stdout} 5 | level {$CADDY_SERVER_LOG_LEVEL:DEBUG} 6 | } 7 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/s6-overlay/s6-rc.d/apache2/run: -------------------------------------------------------------------------------- 1 | #!/command/execlineb -P 2 | with-contenv 3 | 4 | # Set healthcheck sleep delay to 20ms, because you know... Apache 5 | s6-notifyoncheck -s 20 6 | /usr/sbin/apache2ctl -DFOREGROUND -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/ports.conf: -------------------------------------------------------------------------------- 1 | Listen ${APACHE_HTTP_PORT} 2 | 3 | 4 | Listen ${APACHE_HTTPS_PORT} 5 | 6 | 7 | 8 | Listen ${APACHE_HTTPS_PORT} 9 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/sites-available/ssl-mixed: -------------------------------------------------------------------------------- 1 | # HTTP configuration 2 | # 3 | server { 4 | include /etc/nginx/site-opts.d/http.conf; 5 | } 6 | 7 | # HTTPS configuration 8 | # 9 | server { 10 | include /etc/nginx/site-opts.d/https.conf; 11 | } -------------------------------------------------------------------------------- /src/common/usr/local/bin/healthcheck-queue: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Use pgrep to check if the process is running 4 | if pgrep -f "queue:work" > /dev/null 5 | then 6 | exit 0 7 | else 8 | echo "❌ Healthcheck failed: Queue worker process cannot be found." 9 | exit 1 10 | fi -------------------------------------------------------------------------------- /src/common/usr/local/bin/healthcheck-reverb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Use pgrep to check if the process is running 4 | if pgrep -f "reverb:start" > /dev/null 5 | then 6 | exit 0 7 | else 8 | echo "❌ Healthcheck failed: Reverb process cannot be found." 9 | exit 1 10 | fi -------------------------------------------------------------------------------- /src/common/usr/local/bin/healthcheck-schedule: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Use pgrep to check if the process is running 4 | if pgrep -f "schedule:work" > /dev/null 5 | then 6 | exit 0 7 | else 8 | echo "❌ Healthcheck failed: Scheduler process cannot be found." 9 | exit 1 10 | fi -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/sites-available/ssl-mixed.conf: -------------------------------------------------------------------------------- 1 | 2 | Include /etc/apache2/vhost-templates/http.conf 3 | 4 | 5 | 6 | Include /etc/apache2/vhost-templates/https.conf 7 | -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/ssl-mode/mixed.caddyfile: -------------------------------------------------------------------------------- 1 | {$CADDY_HTTP_SERVER_ADDRESS:http://} { 2 | import php-app-common 3 | } 4 | 5 | {$CADDY_HTTPS_SERVER_ADDRESS:https://} { 6 | import auto-https-{$CADDY_AUTO_HTTPS:off} 7 | import php-app-common 8 | import security-https 9 | } 10 | -------------------------------------------------------------------------------- /docs/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>nuxt/renovate-config-nuxt" 4 | ], 5 | "lockFileMaintenance": { 6 | "enabled": true 7 | }, 8 | "packageRules": [{ 9 | "matchDepTypes": ["resolutions"], 10 | "enabled": false 11 | }], 12 | "postUpdateOptions": ["yarnDedupe"] 13 | } -------------------------------------------------------------------------------- /docs/.env.example: -------------------------------------------------------------------------------- 1 | # Core Nuxt Framework Settings 2 | NUXT_APP_BASE_URL=/open-source/docker-php 3 | 4 | # Nuxt SEO Settings 5 | NUXT_SITE_URL=https://localhost:3000/open-source/docker-php 6 | NUXT_SITE_NAME="Docker PHP - Server Side Up" 7 | NUXT_SITE_ENV="development" 8 | 9 | # Analytics 10 | PLAUSIBLE_ENABLED=false -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | 26 | # VSC 27 | .history 28 | -------------------------------------------------------------------------------- /docs/app/components/HeroVideo.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /src/common/usr/local/etc/php/conf.d/zzz-serversideup-docker-php-debug.ini: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; This is an intentionally empty file. It is used as a placeholder 3 | ; for setting proper file permissions when DEBUG mode is enabled. 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | -------------------------------------------------------------------------------- /src/php-fpm.d/usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; This is an intentionally empty file. It is used as a placeholder 3 | ; for setting proper file permissions when DEBUG mode is enabled. 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/heartbeat-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/search-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/logos/x-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/workflows/action_publish-images-dev-main.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish (Dev "Main" Images) 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-dev-images: 8 | uses: ./.github/workflows/service_docker-build-and-publish.yml 9 | with: 10 | registry-repositories: "docker.io/serversideup/php-dev" # Set to our development repository 11 | tag-prefix: '' 12 | release-type: latest 13 | authenticate_with_ghcr: false 14 | secrets: inherit -------------------------------------------------------------------------------- /docs/app/assets/icons/services/nginx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Docker PHP", 3 | "short_name": "Docker PHP", 4 | "icons": [ 5 | { 6 | "src": "/web-app-manifest-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png", 9 | "purpose": "maskable" 10 | }, 11 | { 12 | "src": "/web-app-manifest-512x512.png", 13 | "sizes": "512x512", 14 | "type": "image/png", 15 | "purpose": "maskable" 16 | } 17 | ], 18 | "theme_color": "#000000", 19 | "background_color": "#000000", 20 | "display": "standalone" 21 | } -------------------------------------------------------------------------------- /src/common/usr/local/bin/artisan: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Set default value for APP_BASE_DIR if not already set 4 | APP_BASE_DIR=${APP_BASE_DIR:-/var/www/html} 5 | 6 | # Check if the artisan file exists 7 | if [ ! -f "$APP_BASE_DIR/artisan" ]; then 8 | echo "Error: Artisan file not found in $APP_BASE_DIR" 9 | echo "Please ensure APP_BASE_DIR is set correctly, Laravel is installed and the artisan file is in the expected location." 10 | exit 1 11 | fi 12 | 13 | # Run the PHP artisan command with all provided arguments 14 | php "$APP_BASE_DIR/artisan" "$@" -------------------------------------------------------------------------------- /docs/app/components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 22 | -------------------------------------------------------------------------------- /docs/public/images/logos/nomad.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/app/assets/icons/hosts/hetzner-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/logos/ruby.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: ❓ Support Question 4 | url: https://github.com/serversideup/docker-php/discussions 5 | about: Get friendly support from the community in Discussions. 6 | 7 | - name: ✨ Request a feature 8 | url: https://github.com/serversideup/docker-php/discussions/66 9 | about: Learn how to request a new feature. 10 | 11 | - name: 🤵 Get Professional Support & Customizations 12 | url: https://serversideup.net/professional-support 13 | about: Skip the line and get priority support directly from the creators of Server Side Up. 14 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/heart-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/community-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/app/assets/icons/hosts/digitalocean-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/s6-overlay/s6-rc.d/nginx/data/check: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv sh 2 | curl_options="--fail --location --insecure --silent --output /dev/null" 3 | healthcheck_url="http://localhost:${NGINX_HTTP_PORT}${HEALTHCHECK_PATH}" 4 | 5 | is_online() { 6 | curl $curl_options "$healthcheck_url" 7 | } 8 | 9 | if is_online; then 10 | echo "✅ NGINX + PHP-FPM is running correctly." 11 | exit 0 12 | else 13 | echo "Health check waiting for NGINX + PHP-FPM to start..." 14 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 15 | status_code=$(curl $curl_options -w "%{http_code}" "$healthcheck_url") 16 | echo "HTTP Status Code: $status_code" 17 | fi 18 | exit 1 19 | fi 20 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/s6-overlay/s6-rc.d/apache2/data/check: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv sh 2 | curl_options="--fail --location --insecure --silent --output /dev/null" 3 | healthcheck_url="http://localhost:${APACHE_HTTP_PORT}${HEALTHCHECK_PATH}" 4 | 5 | is_online() { 6 | curl $curl_options "$healthcheck_url" 7 | } 8 | 9 | if is_online; then 10 | echo "✅ Apache + PHP-FPM is running correctly." 11 | exit 0 12 | else 13 | echo "Health check waiting for Apache + PHP-FPM to start..." 14 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 15 | status_code=$(curl $curl_options -w "%{http_code}" "$healthcheck_url") 16 | echo "HTTP Status Code: $status_code" 17 | fi 18 | exit 1 19 | fi 20 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/lightning-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/app/components/Badges.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/docs-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineContentConfig, defineCollection, z } from '@nuxt/content' 2 | 3 | export default defineContentConfig({ 4 | collections: { 5 | landing: defineCollection({ 6 | type: 'page', 7 | source: 'index.md' 8 | }), 9 | docs: defineCollection({ 10 | type: 'page', 11 | source: { 12 | include: '**', 13 | exclude: ['index.md'] 14 | }, 15 | schema: z.object({ 16 | redirect: z.string().optional(), 17 | links: z.array(z.object({ 18 | label: z.string(), 19 | icon: z.string(), 20 | to: z.string(), 21 | target: z.string().optional() 22 | })).optional() 23 | }) 24 | }) 25 | } 26 | }) 27 | -------------------------------------------------------------------------------- /.github/workflows/action_update-dockerhub-readme.yml: -------------------------------------------------------------------------------- 1 | name: Update Docker Hub README 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - README.md 10 | 11 | jobs: 12 | update_container_readme: 13 | runs-on: ubuntu-24.04 14 | name: Push README to Docker Hub 15 | steps: 16 | - name: git checkout 17 | uses: actions/checkout@v5 18 | with: 19 | ref: main 20 | 21 | - name: push README to Dockerhub 22 | uses: peter-evans/dockerhub-description@v4 23 | with: 24 | username: ${{ secrets.DOCKER_HUB_README_USERNAME }} 25 | password: ${{ secrets.DOCKER_HUB_README_PASSWORD }} 26 | repository: serversideup/php 27 | short-description: 'Production-ready PHP Docker images optimized for Laravel and WordPress.' 28 | readme-filepath: 'README.md' -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/server-opts.d/security.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Security Headers 3 | # 4 | 5 | # Prevent IFRAME spoofing attacks 6 | add_header X-Frame-Options "SAMEORIGIN" always; 7 | 8 | # Prevent MIME attacks 9 | add_header X-Content-Type-Options "nosniff" always; 10 | 11 | # Prevent Referrer URL from being leaked 12 | add_header Referrer-Policy "no-referrer-when-downgrade" always; 13 | 14 | # Configure Content Security Policy 15 | # UPDATE - September 2020: Commenting this out until we grasp better security requirements 16 | #add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; 17 | 18 | # Enable HSTS 19 | add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; 20 | 21 | # Prevent access to . files (the well-known directory) 22 | location ~ /\.(?!well-known) { 23 | deny all; 24 | } -------------------------------------------------------------------------------- /docs/app/pages/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 30 | -------------------------------------------------------------------------------- /docs/public/images/logos/python.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/app/error.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 43 | -------------------------------------------------------------------------------- /docs/content/docs/1.getting-started/8.changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | head.title: 'Changelog - Docker PHP - Server Side Up' 3 | description: 'See the latest releases and changes for the PHP Docker Image project.' 4 | layout: docs 5 | --- 6 | ::lead-p 7 | All our changes are documented and published on our GitHub. [See our release notes on GitHub →](https://github.com/serversideup/docker-php/releases){target="_blank"} 8 | :: 9 | 10 | ### Choosing a version 11 | You may want to review [our guide on selecting the right image](/docs/getting-started/choosing-an-image) to determine which version and image tag is best for you. 12 | 13 | :u-button{to="https://github.com/serversideup/docker-php/releases" target="_blank" label="See our release notes on GitHub" aria-label="See our release notes on GitHub" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | The following versions of PHP are being actively updated: 5 | 6 | | PHP Version | Supported | 7 | | ------- | ------------------ | 8 | | 8.5 | :white_check_mark: Active support | 9 | | 8.4 | :white_check_mark: Active support | 10 | | 8.3 | :warning: Security updates only | 11 | | 8.2 | :warning: Security updates only | 12 | | 8.1 | :heavy_exclamation_mark: End of life, update ASAP | 13 | | 8.0 | :heavy_exclamation_mark: End of life, update ASAP | 14 | | 7.4 | :heavy_exclamation_mark: End of life, update ASAP | 15 | | 7.3 | :x: Not supported | 16 | 17 | View the official [PHP supported versions policy](https://www.php.net/supported-versions.php) for more information. 18 | 19 | ## Reporting a Vulnerability 20 | 21 | If you have a vulnerability to report, please follow [our responsible disclosure policy](https://www.notion.so/Responsible-Disclosure-Policy-421a6a3be1714d388ebbadba7eebbdc8). 22 | -------------------------------------------------------------------------------- /docs/app/components/GetStarted.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/mods-available/mpm_event.conf: -------------------------------------------------------------------------------- 1 | # event MPM 2 | # StartServers: initial number of server processes to start 3 | # MinSpareThreads: minimum number of worker threads which are kept spare 4 | # MaxSpareThreads: maximum number of worker threads which are kept spare 5 | # ThreadsPerChild: constant number of worker threads in each server process 6 | # MaxRequestWorkers: maximum number of worker threads 7 | # MaxConnectionsPerChild: maximum number of requests a server process serves 8 | 9 | StartServers ${APACHE_START_SERVERS} 10 | MinSpareThreads ${APACHE_MIN_SPARE_THREADS} 11 | MaxSpareThreads ${APACHE_MAX_SPARE_THREADS} 12 | ThreadLimit ${APACHE_THREAD_LIMIT} 13 | ThreadsPerChild ${APACHE_THREADS_PER_CHILD} 14 | MaxRequestWorkers ${APACHE_MAX_REQUEST_WORKERS} 15 | MaxConnectionsPerChild ${APACHE_MAX_CONNECTIONS_PER_CHILD} 16 | 17 | 18 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 19 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/rocket-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/public/images/logos/twitter-slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/logos/twitter-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/app/assets/icons/hosts/sevalla-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/shield-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/common/usr/local/bin/docker-php-serversideup-install-php-ext-installer: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -oe 3 | 4 | ################################################### 5 | # Usage: docker-php-serversideup-install-php-ext-installer [version] 6 | ################################################### 7 | # This script installs the "install-php-extensions" script from 8 | # https://github.com/mlocati/docker-php-extension-installer 9 | script_name="docker-php-serversideup-install-php-ext-installer" 10 | 11 | ############ 12 | # Environment variables 13 | ############ 14 | PHP_EXT_INSTALLER_VERSION="2.9.18" 15 | 16 | ############ 17 | # Main 18 | ############ 19 | if [ -n "$1" ]; then 20 | PHP_EXT_INSTALLER_VERSION="$1" 21 | fi 22 | 23 | curl -sSLf -o /usr/local/bin/install-php-extensions \ 24 | "https://github.com/mlocati/docker-php-extension-installer/releases/download/$PHP_EXT_INSTALLER_VERSION/install-php-extensions" 25 | chmod +x /usr/local/bin/install-php-extensions 26 | 27 | echo "$script_name: ⚡️ Installed $PHP_EXT_INSTALLER_VERSION of \"install-php-extensions\"" -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/sites-available/ssl-full.template: -------------------------------------------------------------------------------- 1 | # HTTP Redirect configuration 2 | # 3 | server { 4 | listen ${NGINX_HTTP_PORT} default_server; 5 | listen [::]:${NGINX_HTTP_PORT} default_server; 6 | 7 | server_name _; 8 | 9 | absolute_redirect off; 10 | 11 | location / { 12 | set $redirect_to_local_https 0; 13 | 14 | # Check for IPv4 and IPv6 localhost addresses 15 | if ($remote_addr ~ ^127\.0\.0\.1$) { 16 | access_log off; 17 | set $redirect_to_local_https 1; 18 | } 19 | if ($remote_addr ~ ^::1$) { 20 | access_log off; 21 | set $redirect_to_local_https 1; 22 | } 23 | 24 | if ($redirect_to_local_https) { 25 | access_log off; 26 | return 301 https://localhost:${NGINX_HTTPS_PORT}$request_uri; 27 | } 28 | 29 | return 301 https://$host$request_uri; 30 | } 31 | } 32 | 33 | # HTTPS configuration 34 | # 35 | server { 36 | include /etc/nginx/site-opts.d/https.conf; 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/action_publish-images-production.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish (Production Images) 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [released] 7 | schedule: 8 | - cron: '0 8 * * 2' 9 | 10 | jobs: 11 | get-latest-release: 12 | runs-on: ubuntu-24.04 13 | outputs: 14 | release_tag: ${{ steps.get_latest_release.outputs.release_tag }} 15 | steps: 16 | - name: Get Latest Release 17 | id: get_latest_release 18 | run: | 19 | LATEST_TAG=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name) 20 | echo "release_tag=${LATEST_TAG}" >> $GITHUB_OUTPUT 21 | 22 | build-production-images: 23 | needs: get-latest-release 24 | uses: ./.github/workflows/service_docker-build-and-publish.yml 25 | with: 26 | registry-repositories: "docker.io/serversideup/php,ghcr.io/serversideup/php" 27 | tag-prefix: '' 28 | release-type: "latest" 29 | ref: ${{ needs.get-latest-release.outputs.release_tag }} 30 | secrets: inherit 31 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/stars-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/conf-available/remoteip.conf: -------------------------------------------------------------------------------- 1 | RemoteIPHeader CF-Connecting-IP 2 | RemoteIPTrustedProxy 173.245.48.0/20 3 | RemoteIPTrustedProxy 103.21.244.0/22 4 | RemoteIPTrustedProxy 103.22.200.0/22 5 | RemoteIPTrustedProxy 103.31.4.0/22 6 | RemoteIPTrustedProxy 141.101.64.0/18 7 | RemoteIPTrustedProxy 108.162.192.0/18 8 | RemoteIPTrustedProxy 190.93.240.0/20 9 | RemoteIPTrustedProxy 188.114.96.0/20 10 | RemoteIPTrustedProxy 197.234.240.0/22 11 | RemoteIPTrustedProxy 198.41.128.0/17 12 | RemoteIPTrustedProxy 162.158.0.0/15 13 | RemoteIPTrustedProxy 172.64.0.0/13 14 | RemoteIPTrustedProxy 131.0.72.0/22 15 | RemoteIPTrustedProxy 104.16.0.0/13 16 | RemoteIPTrustedProxy 104.24.0.0/14 17 | RemoteIPTrustedProxy 2400:cb00::/32 18 | RemoteIPTrustedProxy 2606:4700::/32 19 | RemoteIPTrustedProxy 2803:f800::/32 20 | RemoteIPTrustedProxy 2405:b500::/32 21 | RemoteIPTrustedProxy 2405:8100::/32 22 | RemoteIPTrustedProxy 2a06:98c0::/29 23 | RemoteIPTrustedProxy 2c0f:f248::/32 24 | RemoteIPTrustedProxy 10.0.0.0/8 25 | RemoteIPTrustedProxy 172.16.0.0/12 26 | RemoteIPTrustedProxy 192.168.0.0/16 -------------------------------------------------------------------------------- /src/variations/frankenphp/etc/frankenphp/ssl-mode/full.caddyfile: -------------------------------------------------------------------------------- 1 | # Healthcheck - HTTP: Redirect to HTTPS 2 | http://localhost:{$CADDY_HTTP_PORT:8080} { 3 | @healthcheck { 4 | remote_ip 127.0.0.1/8 ::1 5 | path /healthcheck # Caddy healthcheck endpoint 6 | path {$HEALTHCHECK_PATH:/healthcheck} # Custom healthcheck endpoint 7 | } 8 | log_skip @healthcheck 9 | redir @healthcheck https://localhost:{$CADDY_HTTPS_PORT:8443}{uri} 308 10 | } 11 | 12 | # Healthcheck - HTTPS: Use self-signed certificate 13 | https://localhost:{$CADDY_HTTPS_PORT:8443} { 14 | tls {$HEALTHCHECK_SSL_CERTIFICATE_FILE:/etc/ssl/healthcheck/localhost.crt} {$HEALTHCHECK_SSL_PRIVATE_KEY_FILE:/etc/ssl/healthcheck/localhost.key} 15 | import php-app-common 16 | import security-https 17 | } 18 | 19 | # HTTP Traffic: Redirect to HTTPS (without explicit port) 20 | {$CADDY_HTTP_SERVER_ADDRESS:http://} { 21 | redir https://{host}{uri} 308 22 | } 23 | 24 | # HTTPS Traffic 25 | {$CADDY_HTTPS_SERVER_ADDRESS:https://} { 26 | import auto-https-{$CADDY_AUTO_HTTPS:off} 27 | import php-app-common 28 | import security-https 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/action_publish-images-beta.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish (Beta Images) 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [prereleased] 7 | schedule: 8 | - cron: '25 8 * * 1' 9 | 10 | jobs: 11 | get-latest-beta-release: 12 | runs-on: ubuntu-24.04 13 | outputs: 14 | release_tag: ${{ steps.get_latest_beta.outputs.release_tag }} 15 | steps: 16 | - name: Get Latest Beta Release 17 | id: get_latest_beta 18 | run: | 19 | LATEST_BETA=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases | jq -r '[.[] | select(.prerelease == true)][0].tag_name') 20 | echo "release_tag=${LATEST_BETA}" >> $GITHUB_OUTPUT 21 | 22 | build-beta-images: 23 | needs: get-latest-beta-release 24 | uses: ./.github/workflows/service_docker-build-and-publish.yml 25 | with: 26 | registry-repositories: "docker.io/serversideup/php,ghcr.io/serversideup/php" 27 | tag-prefix: "beta" 28 | release-type: "testing" 29 | ref: ${{ needs.get-latest-beta-release.outputs.release_tag }} 30 | secrets: inherit -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/nginx.conf.template: -------------------------------------------------------------------------------- 1 | worker_processes auto; 2 | 3 | error_log $NGINX_ERROR_LOG $LOG_OUTPUT_LEVEL; 4 | pid /var/run/nginx.pid; 5 | daemon off; 6 | 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | 13 | http { 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | server_tokens $NGINX_SERVER_TOKENS; 17 | 18 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 19 | '$status $body_bytes_sent "$http_referer" ' 20 | '"$http_user_agent" "$http_x_forwarded_for"'; 21 | 22 | # Disable access logs for healthcheck endpoint 23 | map $request_uri $loggable { 24 | $HEALTHCHECK_PATH 0; 25 | default 1; 26 | } 27 | 28 | access_log $NGINX_ACCESS_LOG main if=$loggable; 29 | 30 | client_max_body_size $NGINX_CLIENT_MAX_BODY_SIZE; 31 | 32 | sendfile on; 33 | #tcp_nopush on; 34 | 35 | keepalive_timeout 65; 36 | 37 | #gzip on; 38 | 39 | include /etc/nginx/conf.d/*.conf; 40 | } 41 | -------------------------------------------------------------------------------- /docs/app/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "@nuxt/ui"; 3 | 4 | @source "../../../content/**/*"; 5 | 6 | @theme { 7 | --color-mode: 'dark'; 8 | --color-mode-fallback: 'dark'; 9 | --color-mode-preference: 'dark'; 10 | --color-mode-fallback: 'dark'; 11 | } 12 | 13 | @theme static { 14 | --ui-header-height: --spacing(24); 15 | --container-8xl: 90rem; 16 | --font-sans: 'Inter', sans-serif; 17 | 18 | --color-green-50: #EFFDF5; 19 | --color-green-100: #D9FBE8; 20 | --color-green-200: #B3F5D1; 21 | --color-green-300: #75EDAE; 22 | --color-green-400: #00DC82; 23 | --color-green-500: #00C16A; 24 | --color-green-600: #00A155; 25 | --color-green-700: #007F45; 26 | --color-green-800: #016538; 27 | --color-green-900: #0A5331; 28 | --color-green-950: #052E16; 29 | } 30 | 31 | :root { 32 | --ui-container: var(--container-8xl); 33 | } 34 | 35 | ul li a:after { 36 | margin-left: 6px; 37 | } 38 | 39 | ul ul ul { 40 | border: none !important; 41 | } 42 | 43 | /* ul ul ul li a:after { 44 | margin-left: -13px; 45 | } 46 | 47 | ul li ul li{ 48 | padding-inline-start: 0; 49 | } */ -------------------------------------------------------------------------------- /src/php-fpm.d/etc/entrypoint.d/5-fpm-pool-user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ################################################### 3 | # Usage: 5-fpm-pool-user.sh 4 | ################################################### 5 | # This script checks if the container is running as root and adds 6 | # the proper user/group configuration to the PHP-FPM pool. 7 | script_name="fpm-pool-user" 8 | 9 | : "${PHP_FPM_CHILD_PROCESS_USER:=www-data}" 10 | : "${PHP_FPM_CHILD_PROCESS_GROUP:=www-data}" 11 | 12 | # Check if running as root 13 | if [ "$(id -u)" -ne 0 ]; then 14 | exit 0 # Exit if not running as root 15 | fi 16 | 17 | # Exit if default config is disabled 18 | if [ "$DISABLE_DEFAULT_CONFIG" = true ]; then 19 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 20 | echo "👉 $script_name: DISABLE_DEFAULT_CONFIG is true, skipping FPM user/group configuration." 21 | fi 22 | exit 0 23 | fi 24 | 25 | # Add user and group configuration to PHP-FPM pool 26 | { 27 | echo "" 28 | echo "user = $PHP_FPM_CHILD_PROCESS_USER" 29 | echo "group = $PHP_FPM_CHILD_PROCESS_GROUP" 30 | } >> /usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf -------------------------------------------------------------------------------- /docs/app/assets/icons/features/logging-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/app/components/Sponsors.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/server-opts.d/remoteip.conf: -------------------------------------------------------------------------------- 1 | ## 2 | # Real IP Addresses 3 | ## 4 | 5 | # Configure docker networks 6 | set_real_ip_from 10.0.0.0/8; 7 | set_real_ip_from 172.16.0.0/12; 8 | set_real_ip_from 192.168.0.0/16; 9 | 10 | # CloudFlare 11 | set_real_ip_from 173.245.48.0/20; 12 | set_real_ip_from 103.21.244.0/22; 13 | set_real_ip_from 103.22.200.0/22; 14 | set_real_ip_from 103.31.4.0/22; 15 | set_real_ip_from 141.101.64.0/18; 16 | set_real_ip_from 108.162.192.0/18; 17 | set_real_ip_from 190.93.240.0/20; 18 | set_real_ip_from 188.114.96.0/20; 19 | set_real_ip_from 197.234.240.0/22; 20 | set_real_ip_from 198.41.128.0/17; 21 | set_real_ip_from 162.158.0.0/15; 22 | set_real_ip_from 104.16.0.0/13; 23 | set_real_ip_from 104.24.0.0/14; 24 | set_real_ip_from 172.64.0.0/13; 25 | set_real_ip_from 131.0.72.0/22; 26 | set_real_ip_from 2400:cb00::/32; 27 | set_real_ip_from 2606:4700::/32; 28 | set_real_ip_from 2803:f800::/32; 29 | set_real_ip_from 2405:b500::/32; 30 | set_real_ip_from 2405:8100::/32; 31 | set_real_ip_from 2a06:98c0::/29; 32 | set_real_ip_from 2c0f:f248::/32; 33 | 34 | # Set RealIP header 35 | real_ip_header CF-Connecting-IP; 36 | real_ip_recursive on; -------------------------------------------------------------------------------- /docs/server/routes/raw/[...slug].md.get.ts: -------------------------------------------------------------------------------- 1 | import { withLeadingSlash } from 'ufo' 2 | import { stringify } from 'minimark/stringify' 3 | import { queryCollection } from '@nuxt/content/nitro' 4 | import type { Collections } from '@nuxt/content' 5 | 6 | export default eventHandler(async (event) => { 7 | const slug = getRouterParams(event)['slug.md'] 8 | if (!slug?.endsWith('.md')) { 9 | throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) 10 | } 11 | 12 | const path = withLeadingSlash(slug.replace('.md', '')) 13 | 14 | const page = await queryCollection(event, 'docs' as keyof Collections).path(path).first() 15 | if (!page) { 16 | throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) 17 | } 18 | 19 | // Add title and description to the top of the page if missing 20 | if (page.body.value[0]?.[0] !== 'h1') { 21 | page.body.value.unshift(['blockquote', {}, page.description]) 22 | page.body.value.unshift(['h1', {}, page.title]) 23 | } 24 | 25 | setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') 26 | return stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' }) 27 | }) 28 | -------------------------------------------------------------------------------- /.github/workflows/action_publish-images-prs.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish (PR Images) 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | pr_number: 7 | description: 'PR number to build (leave empty for manual branch build)' 8 | required: false 9 | type: string 10 | pull_request: 11 | types: [opened, synchronize, reopened] 12 | paths: 13 | - src/** 14 | - .github/workflows/action_publish-images-** 15 | - .github/workflows/service_docker-** 16 | - scripts/** 17 | 18 | jobs: 19 | build-dev-images: 20 | uses: ./.github/workflows/service_docker-build-and-publish.yml 21 | with: 22 | registry-repositories: "docker.io/serversideup/php-dev" 23 | # Use PR number from input if provided, otherwise use the PR event number 24 | tag-prefix: ${{ inputs.pr_number || github.event.pull_request.number }} 25 | release-type: testing 26 | authenticate_with_ghcr: false 27 | push-to-registry: >- 28 | ${{ 29 | github.event_name == 'workflow_dispatch' || 30 | (github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.type == 'Organization') 31 | }} 32 | secrets: inherit 33 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documentation-template", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare", 11 | "lint": "eslint .", 12 | "typecheck": "nuxt typecheck" 13 | }, 14 | "dependencies": { 15 | "@iconify-json/lucide": "^1.2.69", 16 | "@iconify-json/simple-icons": "^1.2.54", 17 | "@iconify-json/vscode-icons": "^1.2.32", 18 | "@nuxt/content": "^3.7.1", 19 | "@nuxt/image": "^1.11.0", 20 | "@nuxt/ui": "^4.0.1", 21 | "@nuxtjs/plausible": "^2.0.1", 22 | "@nuxtjs/sitemap": "^7.4.7", 23 | "@vueuse/core": "^14.0.0", 24 | "@vueuse/nuxt": "^14.0.0", 25 | "better-sqlite3": "^12.4.1", 26 | "nuxt": "^4.1.3", 27 | "nuxt-llms": "0.1.3", 28 | "nuxt-og-image": "^5.1.11", 29 | "nuxt-schema-org": "^5.0.9" 30 | }, 31 | "devDependencies": { 32 | "@nuxt/eslint": "^1.9.0", 33 | "eslint": "^9.37.0", 34 | "typescript": "^5.9.3", 35 | "vue-tsc": "^3.1.1" 36 | }, 37 | "resolutions": { 38 | "unimport": "4.1.1" 39 | }, 40 | "packageManager": "yarn@1.22.22" 41 | } 42 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/sites-available/ssl-full.conf: -------------------------------------------------------------------------------- 1 | 2 | # Configure ServerAdmin and ServerName 3 | ServerName localhost 4 | ServerAdmin webmaster@localhost 5 | 6 | # Set CloudFlare Real IP 7 | RemoteIPHeader CF-Connecting-IP 8 | 9 | # Turn on rewrite engine 10 | RewriteEngine On 11 | 12 | # Redirect traffic from localhost to https://localhost:${APACHE_HTTPS_PORT} 13 | RewriteCond %{SERVER_NAME} =localhost 14 | RewriteRule ^ https://%{SERVER_NAME}:${APACHE_HTTPS_PORT}%{REQUEST_URI} [END,NE,R=permanent] 15 | 16 | # Redirect all other traffic to https://host:443 17 | RewriteCond %{SERVER_NAME} !=localhost 18 | RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L] 19 | 20 | # Set environment variable for healthcheck requests 21 | SetEnvIf Request_URI "^${HEALTHCHECK_PATH}$" dontlog 22 | 23 | # Configure Log Settings 24 | LogFormat "%l %u %t %v %a \"%r\" %>s %b" comonvhost 25 | ErrorLog /dev/stderr 26 | CustomLog /dev/stdout comonvhost env=!dontlog 27 | LogLevel ${LOG_OUTPUT_LEVEL} 28 | 29 | 30 | 31 | 32 | Include /etc/apache2/vhost-templates/https.conf 33 | -------------------------------------------------------------------------------- /docs/public/images/logos/github-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /AGENTS.md: -------------------------------------------------------------------------------- 1 | You are a highly skilled PHP system administrator tasked with maintaining open source PHP Docker images for Laravel applications. Your goal is to assist in creating production-ready Docker images that follow best practices for security, performance, and developer experience using the guidelines below. 2 | 3 | 1. Skills you posses deep knowledge and best practices of: 4 | - Docker 5 | - PHP 6 | - Laravel 7 | - GitHub Actions 8 | - Shell scripting 9 | - S6 Overlay 10 | - Nginx 11 | - Apache 12 | - PHP-FPM 13 | 14 | 2. Development Guidelines: 15 | 16 | - Follow the best practices for security, performance, and developer experience. 17 | - Write clean, maintainable and technically accurate code. 18 | - All entrypoint scripts for the Docker images must be POSIX compliant and able to be executed with /bin/sh. 19 | - Any /bin/sh scripts must be compatible with Debian and Alpine Linux. 20 | - For any /bin/bash scripts, these should work with MacOS, Linux, and WSL2. 21 | - Never use an approach you're not confident about. If you're unsure about something, ask for clarity. 22 | 23 | This project is open source and the code is available on GitHub, so be sure to follow best practices to make it easy for others to understand, modify, and contribute to the project. -------------------------------------------------------------------------------- /docs/public/images/logos/github-slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/server-opts.d/performance.conf: -------------------------------------------------------------------------------- 1 | # favicon.ico 2 | location = /favicon.ico { 3 | log_not_found off; 4 | access_log off; 5 | } 6 | 7 | # robots.txt 8 | location = /robots.txt { 9 | log_not_found off; 10 | access_log off; 11 | # Pass to PHP to ensure PHP apps can handle routes that end in these filetypes 12 | try_files $uri /index.php?$query_string; 13 | } 14 | 15 | # assets, media 16 | location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv|svgz?)$ { 17 | add_header Cache-Control "public, max-age=31536000, immutable"; 18 | access_log off; 19 | log_not_found off; 20 | # Pass to PHP to ensure PHP apps can handle routes that end in these filetypes 21 | try_files $uri /index.php?$query_string; 22 | } 23 | 24 | # fonts 25 | location ~* \.(?:ttf|ttc|otf|eot|woff2?)$ { 26 | add_header Access-Control-Allow-Origin "*"; 27 | add_header Cache-Control "public, max-age=31536000, immutable"; 28 | access_log off; 29 | } 30 | 31 | # gzip 32 | gzip on; 33 | gzip_vary on; 34 | gzip_proxied any; 35 | gzip_comp_level 6; 36 | gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; 37 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/site-opts.d/http.conf.template: -------------------------------------------------------------------------------- 1 | listen ${NGINX_HTTP_PORT} default_server; 2 | listen [::]:${NGINX_HTTP_PORT} default_server; 3 | 4 | root $NGINX_WEBROOT; 5 | 6 | # Set allowed "index" files 7 | index index.html index.htm index.php; 8 | 9 | server_name _; 10 | 11 | charset utf-8; 12 | 13 | absolute_redirect off; 14 | 15 | # Healthcheck: Set /healthcheck to be the static health check URL 16 | location /healthcheck { 17 | access_log off; 18 | 19 | # set max 5 seconds for healthcheck 20 | fastcgi_read_timeout 5s; 21 | 22 | include fastcgi_params; 23 | fastcgi_param SCRIPT_NAME /healthcheck; 24 | fastcgi_param SCRIPT_FILENAME /healthcheck; 25 | fastcgi_pass 127.0.0.1:9000; 26 | } 27 | 28 | # Have NGINX try searching for PHP files as well 29 | location / { 30 | try_files $uri $uri/ /index.php?$query_string; 31 | } 32 | 33 | # Pass "*.php" files to PHP-FPM 34 | location ~ \.php$ { 35 | fastcgi_pass 127.0.0.1:9000; 36 | fastcgi_index index.php; 37 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 38 | include fastcgi_params; 39 | fastcgi_buffers $NGINX_FASTCGI_BUFFERS; 40 | fastcgi_buffer_size $NGINX_FASTCGI_BUFFER_SIZE; 41 | fastcgi_read_timeout $PHP_MAX_EXECUTION_TIME; 42 | } 43 | 44 | # additional config 45 | include /etc/nginx/server-opts.d/*.conf; -------------------------------------------------------------------------------- /docs/public/images/logos/node.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /src/common/usr/local/bin/docker-php-serversideup-dep-install-alpine: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -oe 3 | 4 | ################################################### 5 | # Usage: docker-php-serversideup-dep-install-alpine [alpine-packages] 6 | ################################################### 7 | # This script installs alpine packages that are passed to it 8 | 9 | script_name="docker-php-serversideup-dep-install-alpine" 10 | 11 | ############ 12 | # Sanity checks 13 | ############ 14 | if [ -f /etc/os-release ]; then 15 | # Source the os-release file (including the $NAME variable) 16 | . /etc/os-release 17 | else 18 | echo "🛑 ERROR ($script_name): Unable to determine the OS." 19 | exit 1 20 | fi 21 | 22 | if [ "$NAME" != "Alpine Linux" ] || [ $# -eq 0 ]; then 23 | echo "ℹ️ INFO ($script_name): No arguments were passed or the OS is not Alpine Linux. Continuing..." 24 | exit 0 25 | fi 26 | 27 | ############ 28 | # Functions 29 | ############ 30 | convert_comma_delimited_to_space_separated() { 31 | echo $1 | tr ',' ' ' 32 | } 33 | 34 | ############ 35 | # Main 36 | ############ 37 | DEP_PACKAGES=$(convert_comma_delimited_to_space_separated "$@") 38 | echo "🤖 Installing: $DEP_PACKAGES" 39 | apk update 40 | apk add --no-cache $DEP_PACKAGES 41 | 42 | 43 | echo "🧼 Cleaning up installation of: $DEP_PACKAGES" 44 | rm -rf /var/cache/apk/* 45 | 46 | echo "⚡️ Completed installation of: $DEP_PACKAGES" -------------------------------------------------------------------------------- /docs/public/images/logos/php.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/common/usr/local/bin/docker-php-serversideup-dep-install-debian: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -oe 3 | 4 | ################################################### 5 | # Usage: docker-php-serversideup-dep-install-debian [debian-packages] 6 | ################################################### 7 | # This script installs debian packages that are passed to it 8 | 9 | DEBIAN_FRONTEND=noninteractive 10 | script_name="docker-php-serversideup-dep-install-debian" 11 | 12 | ############ 13 | # Sanity checks 14 | ############ 15 | if [ -f /etc/os-release ]; then 16 | # Source the os-release file (including the $NAME variable) 17 | . /etc/os-release 18 | else 19 | echo "🛑 ERROR ($script_name): Unable to determine the OS." 20 | exit 1 21 | fi 22 | 23 | if [ "$NAME" != "Debian GNU/Linux" ] || [ $# -eq 0 ]; then 24 | echo "ℹ️ INFO ($script_name): No arguments were passed or the OS is not Debian GNU/Linux. Continuing..." 25 | exit 0 26 | fi 27 | 28 | ############ 29 | # Functions 30 | ############ 31 | convert_comma_delimited_to_space_separated() { 32 | echo $1 | tr ',' ' ' 33 | } 34 | 35 | ############ 36 | # Main 37 | ############ 38 | DEP_PACKAGES=$(convert_comma_delimited_to_space_separated "$@") 39 | echo "🤖 Installing: $DEP_PACKAGES" 40 | apt-get update 41 | apt-get install -y $DEP_PACKAGES 42 | 43 | 44 | echo "🧼 Cleaning up installation of: $DEP_PACKAGES" 45 | apt-get clean 46 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 47 | 48 | echo "⚡️ Completed installation of: $DEP_PACKAGES" -------------------------------------------------------------------------------- /docs/app/components/TemplateMenu.vue: -------------------------------------------------------------------------------- 1 | 50 | -------------------------------------------------------------------------------- /src/s6/usr/local/bin/docker-php-serversideup-s6-install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -oue 3 | 4 | ############################################### 5 | # Usage: docker-php-serversideup-s6-install 6 | ############################################### 7 | # This script is used to install S6 Overlay. It is 8 | # intended to be used during the build process only. 9 | # Be sure to set the S6_SRC_URL, S6_SRC_DEP, and S6_DIR 10 | # environment variables before running this script. 11 | 12 | S6_VERSION=v3.2.1.0 13 | mkdir -p $S6_DIR 14 | export SYS_ARCH=$(uname -m) 15 | case "$SYS_ARCH" in 16 | aarch64 ) export S6_ARCH='aarch64' ;; 17 | arm64 ) export S6_ARCH='aarch64' ;; 18 | armhf ) export S6_ARCH='armhf' ;; 19 | arm* ) export S6_ARCH='arm' ;; 20 | i4* ) export S6_ARCH='i486' ;; 21 | i6* ) export S6_ARCH='i686' ;; 22 | s390* ) export S6_ARCH='s390x' ;; 23 | * ) export S6_ARCH='x86_64' ;; 24 | esac 25 | 26 | untar() { 27 | echo "⏬ Downloading $1" 28 | curl -L $1 -o - | tar Jxp -C $S6_DIR 29 | } 30 | 31 | echo "⬇️ Downloading s6 overlay:${S6_ARCH}-${S6_VERSION} for ${SYS_ARCH}" 32 | untar ${S6_SRC_URL}/${S6_VERSION}/s6-overlay-noarch.tar.xz 33 | untar ${S6_SRC_URL}/${S6_VERSION}/s6-overlay-${S6_ARCH}.tar.xz 34 | 35 | # Ensure "php-fpm-healthcheck" is installed 36 | echo "⬇️ Downloading php-fpm-healthcheck..." 37 | curl -o /usr/local/bin/php-fpm-healthcheck https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.6.0/php-fpm-healthcheck 38 | chmod +x /usr/local/bin/php-fpm-healthcheck -------------------------------------------------------------------------------- /docs/public/images/logos/discord-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/logos/discord-slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/vhost-templates/http.conf: -------------------------------------------------------------------------------- 1 | # Configure ServerAdmin and ServerName 2 | ServerName localhost 3 | ServerAdmin webmaster@localhost 4 | 5 | # Set CloudFlare Real IP 6 | RemoteIPHeader CF-Connecting-IP 7 | 8 | # Configure main document root 9 | DocumentRoot ${APACHE_DOCUMENT_ROOT} 10 | 11 | # Set basic settings for document root. Configure correct directory indexes and disable directory browsing 12 | 13 | AllowOverride All 14 | Require all granted 15 | Options -Indexes +FollowSymLinks +MultiViews 16 | DirectoryIndex index.php index.html index.htm 17 | 18 | 19 | # Healthchecks: Set /healthcheck to be the healthcheck URL 20 | ProxyPass "/healthcheck" "fcgi://localhost:9000" 21 | ProxyPassReverse "/healthcheck" "fcgi://localhost:9000" 22 | 23 | # For any files that match PHP, pass it to PHP-FPM for processing 24 | 25 | # 2.4.10+ can proxy to unix socket 26 | ProxyFCGIBackendType GENERIC 27 | SetHandler "proxy:fcgi://localhost:9000" 28 | 29 | 30 | # Set the Proxy Timeout to be 30 minutes 31 | ProxyTimeout 1800 32 | 33 | # Set environment variable for healthcheck requests 34 | SetEnvIf Request_URI "^${HEALTHCHECK_PATH}$" dontlog 35 | 36 | # CustomLog directive to conditionally log requests 37 | LogFormat "%l %u %t %v %a \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" comonvhost 38 | CustomLog /dev/stdout comonvhost env=!dontlog 39 | 40 | # Configure Log Settings 41 | ErrorLog /dev/stderr 42 | LogLevel ${LOG_OUTPUT_LEVEL} 43 | 44 | # Disable Server Signature for increased security 45 | ServerSignature Off -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation & Static Site, generated with Nuxt Content 2 | This is a documentation site built on top of Nuxt Content. 3 | 4 | # Docs location 5 | All docs are located in the [./content](./content/docs) folder if you're just looking for the docs in plain text. 6 | 7 | ## Setup 8 | 9 | Ensure you're in the right directory. 10 | 11 | ```bash 12 | cd docs/ 13 | ``` 14 | 15 | Copy over the environment variable example file. 16 | 17 | ```bash 18 | cp .env.example .env 19 | ``` 20 | 21 | Make sure to install the dependencies: 22 | 23 | ```bash 24 | yarn install 25 | ``` 26 | 27 | ## Development Server 28 | 29 | Start the development server on http://localhost:3000 30 | 31 | ```bash 32 | yarn dev 33 | ``` 34 | 35 | ## Production 36 | 37 | Build the application for production: 38 | 39 | ```bash 40 | yarn build 41 | ``` 42 | 43 | Locally preview production build: 44 | 45 | ```bash 46 | yarn preview 47 | ``` 48 | 49 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 50 | 51 | # Power User Tips 52 | If you're diving deep into the docs, here are some tips to help you out: 53 | 54 | ## Components 55 | All components are from the [Nuxt UI](https://ui.nuxt.com/) component library, using the [Nuxt UI Documentation Template](https://docs-template.nuxt.dev). 56 | 57 | [View the Nuxt UI Documentation Template components →](https://docs-template.nuxt.dev/essentials/prose-components) 58 | 59 | ## Icons 60 | All icons are from the [Lucide](https://lucide.dev/icons/) icon set. Use the icon name of `i-lucide-` as the value for the `icon` field in the YAML frontmatter. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: "\U0001F41B Bug Report" 2 | description: "You found a bug in the code \U0001F914" 3 | labels: ["🧐 Bug: Needs Confirmation"] 4 | assignees: 5 | - jaydrogers 6 | body: 7 | - type: textarea 8 | attributes: 9 | label: Steps To Reproduce 10 | description: Steps to reproduce the behavior. 11 | placeholder: | 12 | 1. In this environment... 13 | 2. With this config... 14 | 3. Run '...' 15 | 4. See error... 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: result 20 | attributes: 21 | label: Outcome 22 | placeholder: Tell us what went wrong 23 | value: | 24 | #### What did you expect? 25 | 26 | #### What happened instead? 27 | validations: 28 | required: true 29 | - type: textarea 30 | attributes: 31 | label: Affected Docker Images 32 | description: "Which images does this issue happen in? (for example: `serversideup/php:8.1-cli`, `serversideup/php:8.1-fpm`, etc). You can also run Run `docker inspect --format='{{json .Config.Labels}}' <>` to get additional information." 33 | validations: 34 | required: true 35 | - type: textarea 36 | attributes: 37 | label: Anything else? 38 | description: | 39 | Links? References? Docker configurations? If there's anything that you can provide to help reproduce your issue faster, please include it. The faster we can reproduce it, the faster we can fix it. 40 | 41 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 42 | validations: 43 | required: false -------------------------------------------------------------------------------- /docs/public/images/logos/amplitude.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AmplitudeJS 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/app/assets/icons/services/docker.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/variations/fpm-nginx/etc/nginx/site-opts.d/https.conf.template: -------------------------------------------------------------------------------- 1 | listen ${NGINX_HTTPS_PORT} ssl default_server; 2 | listen [::]:${NGINX_HTTPS_PORT} ssl default_server; 3 | http2 on; 4 | 5 | root $NGINX_WEBROOT; 6 | 7 | ssl_certificate $SSL_CERTIFICATE_FILE; 8 | ssl_certificate_key $SSL_PRIVATE_KEY_FILE; 9 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; 10 | ssl_protocols TLSv1.2 TLSv1.3; 11 | 12 | # Set allowed "index" files 13 | index index.html index.htm index.php; 14 | 15 | server_name _; 16 | 17 | charset utf-8; 18 | 19 | absolute_redirect off; 20 | 21 | # Healthcheck: Set /healthcheck to be the static health check URL 22 | location /healthcheck { 23 | access_log off; 24 | 25 | # set max 5 seconds for healthcheck 26 | fastcgi_read_timeout 5s; 27 | 28 | include fastcgi_params; 29 | fastcgi_param SCRIPT_NAME /healthcheck; 30 | fastcgi_param SCRIPT_FILENAME /healthcheck; 31 | fastcgi_pass 127.0.0.1:9000; 32 | } 33 | 34 | # Have NGINX try searching for PHP files as well 35 | location / { 36 | try_files $uri $uri/ /index.php?$query_string; 37 | } 38 | 39 | # Pass "*.php" files to PHP-FPM 40 | location ~ \.php$ { 41 | fastcgi_pass 127.0.0.1:9000; 42 | fastcgi_index index.php; 43 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 44 | include fastcgi_params; 45 | fastcgi_buffers $NGINX_FASTCGI_BUFFERS; 46 | fastcgi_buffer_size $NGINX_FASTCGI_BUFFER_SIZE; 47 | fastcgi_read_timeout $PHP_MAX_EXECUTION_TIME; 48 | } 49 | 50 | # additional config 51 | include /etc/nginx/server-opts.d/*.conf; -------------------------------------------------------------------------------- /.github/workflows/scheduled-task_update-sponsors.yml: -------------------------------------------------------------------------------- 1 | name: Generate Sponsors README 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: 30 15 * * 0 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-24.04 9 | steps: 10 | - name: Checkout 🛎️ 11 | uses: actions/checkout@v5 12 | 13 | - name: Generate Sponsors 💖 14 | uses: JamesIves/github-sponsors-readme-action@v1 15 | with: 16 | organization: true 17 | minimum: 4900 18 | maximum: 5100 19 | fallback: 'No bronze sponsors yet. Become a sponsor →' 20 | token: ${{ secrets.SPONSORS_README_ACTION_PERSONAL_ACCESS_TOKEN }} 21 | marker: 'bronze' 22 | template: '{{{ login }}}  ' 23 | file: 'README.md' 24 | 25 | - name: Generate Sponsors 💖 26 | uses: JamesIves/github-sponsors-readme-action@v1 27 | with: 28 | organization: true 29 | maximum: 500 30 | fallback: '

Sponsors

' 31 | token: ${{ secrets.SPONSORS_README_ACTION_PERSONAL_ACCESS_TOKEN }} 32 | marker: 'supporters' 33 | template: '{{{ login }}}  ' 34 | file: 'README.md' 35 | 36 | - name: Deploy to GitHub Pages 🚀 37 | uses: JamesIves/github-pages-deploy-action@v4 38 | with: 39 | branch: main 40 | folder: '.' -------------------------------------------------------------------------------- /docs/public/images/logos/go.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/vhost-templates/https.conf: -------------------------------------------------------------------------------- 1 | # Configure ServerAdmin and ServerName 2 | ServerName localhost 3 | ServerAdmin webmaster@localhost 4 | 5 | # Enable HTTP2 6 | Protocols h2 http/1.1 7 | 8 | SSLProtocol -all +TLSv1.2 +TLSv1.3 9 | 10 | # Set CloudFlare Real IP 11 | RemoteIPHeader CF-Connecting-IP 12 | 13 | # Configure main document root 14 | DocumentRoot ${APACHE_DOCUMENT_ROOT} 15 | 16 | # Set basic settings for document root. Configure correct directory indexes and disable directory browsing 17 | 18 | AllowOverride All 19 | Require all granted 20 | Options -Indexes +FollowSymLinks +MultiViews 21 | DirectoryIndex index.php index.html index.htm 22 | 23 | 24 | # Healthchecks: Set /healthcheck to be the healthcheck URL 25 | ProxyPass "/healthcheck" "fcgi://localhost:9000" 26 | ProxyPassReverse "/healthcheck" "fcgi://localhost:9000" 27 | 28 | # For any files that match PHP, pass it to PHP-FPM for processing 29 | 30 | # 2.4.10+ can proxy to unix socket 31 | ProxyFCGIBackendType GENERIC 32 | SetHandler "proxy:fcgi://localhost:9000" 33 | 34 | 35 | # Set the Proxy Timeout to be 30 minutes 36 | ProxyTimeout 1800 37 | 38 | # Set environment variable for healthcheck requests 39 | SetEnvIf Request_URI "^${HEALTHCHECK_PATH}$" dontlog 40 | 41 | # CustomLog directive to conditionally log requests 42 | LogFormat "%l %u %t %v %a \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" comonvhost 43 | CustomLog /dev/stdout comonvhost env=!dontlog 44 | 45 | # Configure Log Settings 46 | ErrorLog /dev/stderr 47 | LogLevel ${LOG_OUTPUT_LEVEL} 48 | 49 | # Disable Server Signature for increased security 50 | ServerSignature Off 51 | 52 | # SSL Settings 53 | SSLEngine on 54 | SSLCertificateFile ${SSL_CERTIFICATE_FILE} 55 | SSLCertificateKeyFile ${SSL_PRIVATE_KEY_FILE} 56 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/cloudflare-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/app/assets/icons/hosts/vultr-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/app/components/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 77 | -------------------------------------------------------------------------------- /docs/app/app.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 68 | -------------------------------------------------------------------------------- /docs/app/assets/icons/features/php-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/conf-available/serversideup.conf: -------------------------------------------------------------------------------- 1 | # 2 | # ServerName gives the name and port that the server uses to identify itself. 3 | # This can often be determined automatically, but we recommend you specify 4 | # it explicitly to prevent problems during startup. 5 | # 6 | # If your host doesn't have a registered DNS name, enter its IP address here. 7 | ServerName localhost 8 | 9 | # 10 | # ErrorLog: The location of the error log file. 11 | # If you do not specify an ErrorLog directive within a 12 | # container, error messages relating to that virtual host will be 13 | # logged here. If you *do* define an error logfile for a 14 | # container, that host's errors will be logged there and not here. 15 | # 16 | ErrorLog /dev/stderr 17 | 18 | # 19 | # LogLevel: Control the number of messages logged to the error_log. 20 | # Possible values include: debug, info, notice, warn, error, crit, 21 | # alert, emerg. 22 | # 23 | LogLevel ${LOG_OUTPUT_LEVEL} 24 | 25 | 26 | # 27 | # The following directives define some format nicknames for use with 28 | # a CustomLog directive (see below). 29 | # 30 | LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined 31 | LogFormat "%a %l %u %t \"%r\" %>s %b" common 32 | 33 | 34 | # You need to enable mod_logio.c to use %I and %O 35 | LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio 36 | 37 | 38 | # 39 | # The location and format of the access logfile (Common Logfile Format). 40 | # If you do not define any access logfiles within a 41 | # container, they will be logged here. Contrariwise, if you *do* 42 | # define per- access logfiles, transactions will be 43 | # logged therein and *not* in this file. 44 | # 45 | #CustomLog logs/access.log common 46 | 47 | # 48 | # If you prefer a logfile with access, agent, and referer information 49 | # (Combined Logfile Format) you can use the following directive. 50 | # 51 | CustomLog "|/bin/cat" combined 52 | -------------------------------------------------------------------------------- /scripts/generate-matrix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Usage: generate-matrix.sh [path/to/php-versions.yml] 5 | # Reads the provided YAML (or $PHP_VERSIONS_FILE, or default scripts/conf/php-versions.yml) 6 | # and prints a GitHub Actions matrix JSON of the form {"include": [...]} 7 | 8 | PHP_VERSIONS_FILE="${1:-${PHP_VERSIONS_FILE:-scripts/conf/php-versions.yml}}" 9 | 10 | if [ ! -f "$PHP_VERSIONS_FILE" ]; then 11 | echo "YAML file not found: $PHP_VERSIONS_FILE" >&2 12 | exit 1 13 | fi 14 | 15 | # Convert YAML to JSON, then shape it with jq. 16 | yq -o=json "$PHP_VERSIONS_FILE" | jq -c ' 17 | 18 | def version_weight: 19 | # Support numeric patches x.y.z and RC minors x.y-rc 20 | if test("-rc$") then 21 | capture("^(?[0-9]+)\\.(?[0-9]+)-rc$") as $m 22 | | ($m.maj|tonumber)*10000 + ($m.min|tonumber)*100 + 99 23 | else 24 | capture("^(?[0-9]+)\\.(?[0-9]+)\\.(?[0-9]+)$") as $m 25 | | ($m.maj|tonumber)*10000 + ($m.min|tonumber)*100 + ($m.pat|tonumber) 26 | end; 27 | 28 | def os_family_match($os_name; $supported): 29 | # Allow listing "alpine" to include any alpine3.xx base_os 30 | # Exact matches like "bullseye", "bookworm", "trixie" must match exactly 31 | ($supported == $os_name) or ($supported == "alpine" and ($os_name | startswith("alpine"))); 32 | 33 | def is_supported($variation; $os): 34 | # If no supported_os specified, allow all; otherwise filter 35 | (($variation.supported_os // []) | length) == 0 or 36 | ((($variation.supported_os // []) | any(os_family_match($os.name; .)))); 37 | 38 | . as $root 39 | | [ ($root.php_variations[] | {name, supported_os, excluded_minor_versions}) as $variation 40 | | $root.php_versions[] 41 | | .minor_versions[] as $minor 42 | | select((($variation.excluded_minor_versions // []) | index($minor.minor)) | not) 43 | | $minor.base_os[] as $os 44 | | $minor.patch_versions[] as $patch 45 | | select(is_supported($variation; $os)) 46 | | {patch_version: $patch, base_os: $os.name, php_variation: $variation.name} 47 | ] 48 | | { include: ( . | sort_by(.patch_version | version_weight) | reverse ) } 49 | ' -------------------------------------------------------------------------------- /docs/app/assets/icons/features/nginx-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/app/components/PageHeaderLinks.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 84 | -------------------------------------------------------------------------------- /docs/content/docs/3.framework-guides/1.laravel/3.queue.md: -------------------------------------------------------------------------------- 1 | --- 2 | head.title: 'Laravel Queue with Docker - Docker PHP - Server Side Up' 3 | description: 'Learn how to configure a Laravel Queue with Docker.' 4 | layout: docs 5 | title: Queue 6 | --- 7 | 8 | ## Laravel Queue with Docker 9 | Run Laravel queue workers by passing the Artisan queue command as the container's command. This allows you to scale queue workers independently from your web server. 10 | 11 | ## Docker Compose example 12 | 13 | ::tip{to="https://getspin.pro/docs/services/laravel-scheduler" target="_blank"} 14 | Want to skip the setup? [Spin Pro](https://getspin.pro/docs/services/laravel-scheduler){target="_blank"} handles Laravel queue workers on your VPS with Docker and zero-downtime deployments—all configured for you. 15 | :: 16 | 17 | This example runs a dedicated queue container using the same image as your web service. Use the full path to Artisan (`/var/www/html/artisan`) when defining the container command. 18 | 19 | **Key points:** 20 | - Use the same image for both your web and queue services 21 | - Set `SIGTERM` as the stop signal for graceful shutdown (especially for `fpm-apache` and `fpm-nginx`) 22 | - Include a health check to monitor queue worker status 23 | 24 | ```yml [compose.yml] 25 | services: 26 | php: 27 | image: my/laravel-app 28 | 29 | queue: 30 | image: my/laravel-app 31 | command: ["php", "/var/www/html/artisan", "queue:work", "--tries=3"] 32 | stop_signal: SIGTERM 33 | healthcheck: 34 | # This is our native healthcheck script for the queue 35 | test: ["CMD", "healthcheck-queue"] 36 | start_period: 10s 37 | ``` 38 | 39 | ## Advanced configuration 40 | 41 | **Graceful shutdown:** The `SIGTERM` signal ensures queue workers finish processing current jobs before stopping. This is especially important for `fpm-apache` and `fpm-nginx` images. 42 | 43 | ::tip 44 | **Multiple processes in one container:** If you're running `fpm-nginx` or `fpm-apache` and you'd like to have everything in a single container, you can [write your own S6 Overlay service script](https://github.com/just-containers/s6-overlay/tree/master#writing-a-service-script){target="_blank"} to properly manage multiple processes in a single container. Learn more about about this in our [Using S6 Overlay guide](/docs/guide/using-s6-overlay). 45 | :: 46 | -------------------------------------------------------------------------------- /src/common/etc/entrypoint.d/0-container-info.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "$SHOW_WELCOME_MESSAGE" = "false" ] || [ "$LOG_OUTPUT_LEVEL" = "off" ] || [ "$DISABLE_DEFAULT_CONFIG" = "true" ]; then 3 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 4 | echo "👉 $0: Container info was display was skipped." 5 | fi 6 | # Skip the rest of the script 7 | exit 0 8 | fi 9 | 10 | # Get OPcache status 11 | PHP_OPCACHE_STATUS=$(php -r 'echo ini_get("opcache.enable");') 12 | 13 | if [ "$PHP_OPCACHE_STATUS" = "1" ]; then 14 | PHP_OPCACHE_MESSAGE="✅ Enabled" 15 | else 16 | PHP_OPCACHE_MESSAGE="❌ Disabled" 17 | fi 18 | 19 | # Get memory limits 20 | MEMORY_LIMIT=$(php -r 'echo ini_get("memory_limit");') 21 | UPLOAD_LIMIT=$(php -r 'echo ini_get("upload_max_filesize");') 22 | 23 | echo ' 24 | -------------------------------------------------------------------- 25 | ____ ____ _ _ _ _ 26 | / ___| ___ _ ____ _____ _ __ / ___|(_) __| | ___ | | | |_ __ 27 | \___ \ / _ \ __\ \ / / _ \ __| \___ \| |/ _` |/ _ \ | | | | _ \ 28 | ___) | __/ | \ V / __/ | ___) | | (_| | __/ | |_| | |_) | 29 | |____/ \___|_| \_/ \___|_| |____/|_|\__,_|\___| \___/| .__/ 30 | |_| 31 | 32 | Brought to you by serversideup.net 33 | -------------------------------------------------------------------- 34 | 35 | 📚 Documentation: https://serversideup.net/php/docs 36 | 💬 Get Help: https://serversideup.net/php/community 37 | 🙌 Sponsor: https://serversideup.net/sponsor 38 | 39 | ------------------------------------- 40 | ℹ️ Container Information 41 | ------------------------------------- 42 | 📦 Versions 43 | • Image: '"$(cat /etc/serversideup-php-version)"' 44 | • PHP: '"$(php -r 'echo phpversion();')"' 45 | • OS: '"$(. /etc/os-release; echo "${PRETTY_NAME}")"' 46 | 47 | 👤 Container User 48 | • User: '"$(whoami)"' 49 | • UID: '"$(id -u)"' 50 | • GID: '"$(id -g)"' 51 | 52 | ⚡ Performance 53 | • OPcache: '"$PHP_OPCACHE_MESSAGE"' 54 | • Memory Limit: '"$MEMORY_LIMIT"' 55 | • Upload Limit: '"$UPLOAD_LIMIT"' 56 | 57 | 🔄 Runtime 58 | • Docker CMD: '"$DOCKER_CMD"' 59 | ' 60 | 61 | if [ "$PHP_OPCACHE_STATUS" = "0" ]; then 62 | echo "👉 [NOTICE]: Improve PHP performance by setting PHP_OPCACHE_ENABLE=1 (recommended for production)." 63 | fi -------------------------------------------------------------------------------- /src/common/usr/local/bin/docker-php-serversideup-entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Initialize variables 5 | SERVERSIDEUP_DEFAULT_COMMAND="false" 6 | S6_INITIALIZED="false" 7 | 8 | # Enable debug mode if LOG_OUTPUT_LEVEL is set to "debug" 9 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 10 | echo "🔥🔥🔥 DEBUG MODE has been set. Get ready for a ton of debug log output..." 11 | set -x 12 | fi 13 | 14 | # Check if the default command is being used 15 | case "$1" in 16 | "/init" | "php-fpm") 17 | SERVERSIDEUP_DEFAULT_COMMAND="true" 18 | ;; 19 | esac 20 | 21 | # Check if S6 overlay is initialized 22 | if [ -d "/etc/s6-overlay" ] && [ "$SERVERSIDEUP_DEFAULT_COMMAND" = "true" ]; then 23 | S6_INITIALIZED="true" 24 | fi 25 | 26 | # Export variables 27 | export SERVERSIDEUP_DEFAULT_COMMAND 28 | export S6_INITIALIZED 29 | 30 | # Export the CMD for use in initialization scripts 31 | export DOCKER_CMD="$*" 32 | 33 | ############################################### 34 | # Usage: docker-php-serversideup-entrypoint 35 | ############################################### 36 | # This script is used to execute scripts from "/etc/entrypoint.d" and then 37 | # execute the CMD passed in from the Dockerfile. 38 | 39 | # Execute scripts from /etc/entrypoint.d/ in numeric order 40 | find /etc/entrypoint.d/ -type f -name '*.sh' | sort -V | while IFS= read -r f; do 41 | if [ -e "$f" ]; then 42 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 43 | echo "🔄 Executing initialization script: $f" 44 | fi 45 | 46 | # Source the script in a subshell to contain exits while preserving environment 47 | (. "$f") 48 | exit_code=$? 49 | 50 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 51 | echo "📝 Script $f completed with exit code: $exit_code" 52 | fi 53 | 54 | # Only stop on actual errors (non-zero exit codes) 55 | if [ $exit_code -ne 0 ]; then 56 | echo "❌ Error: Initialization script $f failed with exit code $exit_code" >&2 57 | exit $exit_code 58 | fi 59 | else 60 | echo "⚠️ Warning: Initialization script $f not found" >&2 61 | fi 62 | done 63 | 64 | # first arg is `-f` or `--some-option` 65 | if [ "${1#-}" != "$1" ]; then 66 | set -- php "$@" 67 | fi 68 | 69 | # Execute the CMD passed in from the Dockerfile 70 | exec "$@" 71 | -------------------------------------------------------------------------------- /docs/content/docs/3.framework-guides/1.laravel/4.horizon.md: -------------------------------------------------------------------------------- 1 | --- 2 | head.title: 'Laravel Horizon with Docker - Docker PHP - Server Side Up' 3 | description: 'Learn how to configure Laravel Horizon with Docker.' 4 | layout: docs 5 | title: Horizon 6 | --- 7 | 8 | ## Laravel Horizon with Docker 9 | Run Laravel Horizon by passing the Artisan command as the container's command. Horizon provides a dashboard and monitoring for your Redis queues. 10 | 11 | ## Docker Compose example 12 | 13 | ::tip{to="https://getspin.pro/docs/services/laravel-scheduler" target="_blank"} 14 | Want to skip the setup? [Spin Pro](https://getspin.pro/docs/services/laravel-scheduler){target="_blank"} handles Laravel Horizon on your VPS with Docker and zero-downtime deployments—all configured for you. 15 | :: 16 | 17 | This example runs a dedicated Horizon container using the same image as your web service. Horizon requires Redis to be running and properly configured in your `.env` file. 18 | 19 | **Key points:** 20 | - Use the same image for both your web and Horizon services 21 | - Ensure Redis is running before starting Horizon 22 | - Configure Redis connection details in your `.env` file 23 | - Set `SIGTERM` as the stop signal for graceful shutdown (especially for `fpm-apache` and `fpm-nginx`) 24 | - Include a health check to monitor Horizon status 25 | 26 | ```yml [compose.yml] 27 | services: 28 | php: 29 | image: my/laravel-app 30 | 31 | redis: 32 | image: redis:6 33 | command: "redis-server --appendonly yes --requirepass redispassword" 34 | 35 | horizon: 36 | image: my/laravel-app 37 | command: ["php", "/var/www/html/artisan", "horizon"] 38 | stop_signal: SIGTERM 39 | healthcheck: 40 | test: ["CMD", "healthcheck-horizon"] 41 | start_period: 10s 42 | ``` 43 | 44 | ## Advanced configuration 45 | 46 | **Graceful shutdown:** The `SIGTERM` signal ensures Horizon finishes processing current jobs before stopping. This is especially important for `fpm-apache` and `fpm-nginx` images. 47 | 48 | ::tip 49 | **Multiple processes in one container:** If you're running `fpm-nginx` or `fpm-apache` and you'd like to have everything in a single container, you can [write your own S6 Overlay service script](https://github.com/just-containers/s6-overlay/tree/master#writing-a-service-script){target="_blank"} to properly manage multiple processes in a single container. Learn more about about this in our [Using S6 Overlay guide](/docs/guide/using-s6-overlay). 50 | :: -------------------------------------------------------------------------------- /docs/modules/pre-render-raw-routes.ts: -------------------------------------------------------------------------------- 1 | import { addPrerenderRoutes, defineNuxtModule, createResolver } from '@nuxt/kit' 2 | import { readdirSync, statSync } from 'fs' 3 | import { join, relative } from 'path' 4 | 5 | export default defineNuxtModule({ 6 | meta: { 7 | name: 'pre-render-raw-routes', 8 | configKey: 'preRenderRawRoutes', 9 | }, 10 | setup(options, nuxt) { 11 | const { resolve } = createResolver(import.meta.url) 12 | const contentDir = resolve(nuxt.options.rootDir, 'content/docs') 13 | 14 | // Remove leading number and dot from names (e.g., "1.getting-started" -> "getting-started") 15 | function cleanName(name: string): string { 16 | return name.replace(/^\d+\./, '') 17 | } 18 | 19 | // Recursively get all .md files 20 | function getMarkdownFiles(dir: string, basePath: string = ''): string[] { 21 | const files: string[] = [] 22 | try { 23 | const entries = readdirSync(dir, { withFileTypes: true }) 24 | for (const entry of entries) { 25 | const fullPath = join(dir, entry.name) 26 | const cleanedName = cleanName(entry.name) 27 | const relativePath = join(basePath, cleanedName) 28 | 29 | if (entry.isDirectory()) { 30 | files.push(...getMarkdownFiles(fullPath, relativePath)) 31 | } else if (entry.isFile() && entry.name.endsWith('.md') && entry.name.includes('index.md')) { 32 | // For index.md, use the directory path (basePath) instead of including "index" 33 | const route = basePath ? `/${basePath}` : '/' 34 | files.push(route) 35 | } else if (entry.isFile() && entry.name.endsWith('.md') && entry.name !== 'index.md') { 36 | const route = `/${relativePath.replace(/\.md$/, '')}` 37 | files.push(route) 38 | } 39 | } 40 | } catch (error) { 41 | // Directory might not exist or be accessible 42 | console.warn(`Could not read directory ${dir}:`, error) 43 | } 44 | return files 45 | } 46 | 47 | const routes = getMarkdownFiles(contentDir) 48 | console.log(routes); 49 | const rawRoutes = routes.map(route => `/raw/docs${route}.md`) 50 | 51 | addPrerenderRoutes(rawRoutes) 52 | }, 53 | }) 54 | -------------------------------------------------------------------------------- /docs/app/layouts/docs.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | -------------------------------------------------------------------------------- /src/utilities-webservers/etc/entrypoint.d/5-generate-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ################################################### 3 | # Usage: 5-generate-ssl.sh 4 | ################################################### 5 | # This script generates a self-signed SSL certificate and key for the container. 6 | script_name="generate-ssl" 7 | 8 | SSL_CERTIFICATE_FILE=${SSL_CERTIFICATE_FILE:-"/etc/ssl/private/self-signed-web.crt"} 9 | SSL_PRIVATE_KEY_FILE=${SSL_PRIVATE_KEY_FILE:-"/etc/ssl/private/self-signed-web.key"} 10 | SSL_MODE=${SSL_MODE:-"off"} 11 | HEALTHCHECK_SSL_CERTIFICATE_FILE=${HEALTHCHECK_SSL_CERTIFICATE_FILE:-"/etc/ssl/healthcheck/localhost.crt"} 12 | HEALTHCHECK_SSL_PRIVATE_KEY_FILE=${HEALTHCHECK_SSL_PRIVATE_KEY_FILE:-"/etc/ssl/healthcheck/localhost.key"} 13 | 14 | if [ "$SSL_MODE" = "off" ]; then 15 | echo "ℹ️ NOTICE ($script_name): SSL mode is off, so we won't generate a self-signed SSL key pair." 16 | return 0 17 | fi 18 | 19 | if [ -z "$SSL_CERTIFICATE_FILE" ] || [ -z "$SSL_PRIVATE_KEY_FILE" ]; then 20 | echo "🛑 ERROR ($script_name): SSL_CERTIFICATE_FILE or SSL_PRIVATE_KEY_FILE is not set." 21 | return 1 22 | fi 23 | 24 | if [ -f "$SSL_CERTIFICATE_FILE" ] && [ ! -f "$SSL_PRIVATE_KEY_FILE" ] || 25 | [ ! -f "$SSL_CERTIFICATE_FILE" ] && [ -f "$SSL_PRIVATE_KEY_FILE" ]; then 26 | echo "🛑 ERROR ($script_name): Only one of the SSL certificate or private key exists. Check the SSL_CERTIFICATE_FILE and SSL_PRIVATE_KEY_FILE variables and try again." 27 | echo "🛑 ERROR ($script_name): SSL_CERTIFICATE_FILE: $SSL_CERTIFICATE_FILE" 28 | echo "🛑 ERROR ($script_name): SSL_PRIVATE_KEY_FILE: $SSL_PRIVATE_KEY_FILE" 29 | return 1 30 | fi 31 | 32 | # Generate self-signed Healthcheck SSL keypair for FrankenPHP only 33 | if [ -d "/etc/frankenphp/" ]; then 34 | echo "🔐 Generating self-signed Healthcheck SSL keypair..." 35 | openssl req -x509 \ 36 | -subj "/CN=localhost" \ 37 | -nodes -newkey rsa:2048 \ 38 | -keyout "$HEALTHCHECK_SSL_PRIVATE_KEY_FILE" \ 39 | -out "$HEALTHCHECK_SSL_CERTIFICATE_FILE" \ 40 | -days 365 >/dev/null 2>&1 41 | fi 42 | 43 | if [ -f "$SSL_CERTIFICATE_FILE" ] && [ -f "$SSL_PRIVATE_KEY_FILE" ]; then 44 | echo "ℹ️ NOTICE ($script_name): SSL certificate and private key already exist, so we'll use the existing files." 45 | return 0 46 | fi 47 | 48 | echo "🔐 Default SSL Keypair not found. Generating self-signed SSL keypair..." 49 | openssl req -x509 \ 50 | -subj "/CN=localhost" \ 51 | -nodes -newkey rsa:2048 \ 52 | -keyout "$SSL_PRIVATE_KEY_FILE" \ 53 | -out "$SSL_CERTIFICATE_FILE" \ 54 | -days 365 >/dev/null 2>&1 55 | exit 0 -------------------------------------------------------------------------------- /src/common/usr/local/bin/docker-php-serversideup-set-id: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | ################################################### 5 | # Usage: docker-php-serversideup-set-id [username] [uid]:[gid] 6 | ################################################### 7 | # This script is intended to be called on build for sysadmins who want to 8 | # change the UID and GID of a specific user in a format [uid]:[gid]. This is useful for when you 9 | # want to match the UID and GID of the host machine to the container. 10 | # Specifically, this can be helpful to call during a build target in development 11 | # so developers don't need to worry about permissions issues. 12 | script_name="docker-php-serversideup-set-id" 13 | 14 | # Sanity checks 15 | if [ "$#" -ne 2 ]; then 16 | echo "Usage: $script_name [username] [uid]:[gid]" 17 | exit 1 18 | fi 19 | 20 | # Check for root privileges 21 | if [ "$(id -u)" -ne 0 ]; then 22 | echo "${script_name}: This script must be run as root within the container. Be sure to set \"USER root\" in your Dockerfile before running this script." 23 | exit 1 24 | fi 25 | 26 | username="$1" 27 | uid_gid="$2" 28 | 29 | # Split UID and GID 30 | uid=$(echo "$uid_gid" | cut -d':' -f1) 31 | gid=$(echo "$uid_gid" | cut -d':' -f2) 32 | 33 | # Verify that uid and gid are numeric 34 | if ! echo "$uid" | grep -qE '^[0-9]+$' || ! echo "$gid" | grep -qE '^[0-9]+$'; then 35 | echo "$script_name: UID and GID must be numeric." 36 | exit 1 37 | fi 38 | 39 | # Check if the user exists 40 | if ! id "$username" > /dev/null 2>&1; then 41 | echo "$script_name: User \"$username\" does not exist." 42 | exit 1 43 | fi 44 | 45 | current_uid=$(id -u "$username" 2>/dev/null) 46 | current_gid=$(id -g "$username" 2>/dev/null) 47 | 48 | # Exit if the UID and GID are already set 49 | if [ "$current_uid" -eq "$uid" ] && [ "$current_gid" -eq "$gid" ]; then 50 | echo "$script_name: User $username already has UID $uid and GID $gid." 51 | exit 0 52 | fi 53 | 54 | # Check if another group has the GID already 55 | if getent group "$gid" > /dev/null; then 56 | moved_group_id="99$gid" 57 | existing_group_name=$(getent group "$gid" | cut -d: -f1) 58 | echo "$script_name: ⚡️ Moving GID of $existing_group_name to $moved_group_id" 59 | groupmod -g "$moved_group_id" "$existing_group_name" 60 | fi 61 | 62 | # Check if another user has the UID already 63 | if getent passwd "$uid" > /dev/null; then 64 | moved_user_id="99$uid" 65 | echo "$script_name: ⚡️ Moving UID of $username to $moved_user_id" 66 | usermod -u "$moved_user_id" "$username" 67 | fi 68 | 69 | # Change the UID and GID 70 | groupmod -g "$gid" "$username" 71 | usermod -u "$uid" "$username" 72 | 73 | echo "$script_name: ✅ Set $username UID to $uid and GID to $gid." -------------------------------------------------------------------------------- /docs/content/docs/6.customizing-the-image/1.changing-common-php-settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | head.title: 'Changing common PHP settings - Docker PHP - Server Side Up' 3 | description: 'Learn how to change common PHP settings with environment variables or your own php.ini file.' 4 | layout: docs 5 | title: Changing php.ini settings 6 | --- 7 | 8 | ::lead-p 9 | Instead of going through the effort of writing custom scripts or mounting files to change PHP settings, have the power to change common settings with the simplicity of an environment variable. 10 | :: 11 | 12 | ## Common Examples 13 | All our environment variables are documented and can be found in our environment variable specification documentation. 14 | 15 | :u-button{to="/docs/reference/environment-variable-specification" label="Environment Variable Specification" aria-label="Environment Variable Specification" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} 16 | 17 | Here are a few examples on how you can change common PHP settings. 18 | 19 | ```yml [compose.yml] {4-6} 20 | services: 21 | php: 22 | image: serversideup/php:8.2.12-fpm-nginx-bookworm 23 | environment: 24 | PHP_POST_MAX_SIZE: "500M" 25 | PHP_UPLOAD_MAX_FILE_SIZE: "500M" 26 | SSL_MODE: "mixed" 27 | ports: 28 | - 80:8080 29 | - 443:8443 30 | volumes: 31 | - .:/var/www/html/ 32 | ``` 33 | 34 | You can also adjust environment variables using the Docker CLI. 35 | 36 | ```bash [Terminal] 37 | docker run -d \ 38 | -p 80:8080 \ 39 | -v $(pwd):/var/www/html \ 40 | -e PHP_DATE_TIMEZONE="America/New_York" \ 41 | serversideup/php:8.2.12-fpm-nginx-bookworm 42 | ``` 43 | 44 | ## Setting your own php.ini 45 | PHP will read the `php.ini` file from the `/usr/local/etc/php/conf.d/` directory in alphabetical order. This means you can create your own `php.ini` file and mount it to the container to override the default settings. 46 | 47 | For example, we can create this file in our project directory: 48 | ```ini [zzz-custom-php.ini] 49 | mysqli.max_persistent = 300 50 | opcache.max_file_size = 10M 51 | opcache.log_verbosity_level = 3 52 | ``` 53 | 54 | Then in our Dockerfile, we can copy this file to the `/usr/local/etc/php/conf.d/` directory: 55 | ```dockerfile [Dockerfile] 56 | FROM serversideup/php:8.5-fpm-nginx-bookworm 57 | 58 | COPY zzz-custom-php.ini /usr/local/etc/php/conf.d/ 59 | ``` 60 | 61 | If you prefer to remove the default `php.ini` file, you can do so by adding the following line to your Dockerfile: 62 | 63 | ```dockerfile [Dockerfile] 64 | FROM serversideup/php:8.5-fpm-nginx-bookworm 65 | 66 | RUN rm /usr/local/etc/php/conf.d/serversideup-docker-php.ini 67 | COPY zzz-custom-php.ini /usr/local/etc/php/conf.d/ 68 | ``` 69 | 70 | ## Validating changes 71 | It's always best to validate your changes by running `php -i` via the command line or using [`phpinfo()`](https://www.php.net/manual/en/function.phpinfo.php){target="_blank"}. -------------------------------------------------------------------------------- /docs/content/docs/2.image-variations/cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI 3 | description: 'Learn how to use the CLI variation of the serversideup/php image.' 4 | --- 5 | 6 | ::lead-p 7 | The CLI variation is a minimal image designed for running PHP from the command line only. It does not include a web server. 8 | 9 | Use this variation for running commands like Composer, running one-off scripts, or executing PHP commands that don't require a web server. 10 | :: 11 | 12 | ## When to Use CLI 13 | Use the CLI variation when you need to: 14 | 15 | - Run Composer for dependency management 16 | - Execute one-off PHP scripts 17 | - Need a very small image size 18 | 19 | #### Perfect for 20 | - Running PHP locally without needing to install PHP on your host system. 21 | 22 | #### What's Inside 23 | 24 | | Item | Status | 25 | |------|--------| 26 | | PHP CLI binary | ✅ | 27 | | Common PHP extensions | ✅ | 28 | | `composer` executable | ✅ | 29 | | `install-php-extensions` script | ✅ | 30 | | Essential system utilities | ✅ | 31 | | Native health checks | ❌ | 32 | | Web server | ❌ (no web server included) | 33 | | Process management | Single entrypoint, single process | 34 | | Exposed Ports | None | 35 | | Stop Signal | `SIGTERM` | 36 | 37 | ## Quick Start 38 | Here are a few quick examples to get you started. 39 | 40 | ### Docker CLI 41 | ```bash [Terminal] 42 | docker run -it -v $(pwd):/var/www/html serversideup/php:cli bash 43 | ``` 44 | 45 | The above command will mount your current directory as the `/var/www/html` directory in the container and open a bash shell inside the container where PHP is installed. To exit, just type `exit`. 46 | 47 | ### Docker Compose 48 | If you want something more repeatable, you can use Docker Compose to start a container with the CLI variation and mount your current directory as the `/var/www/html` directory in the container. 49 | 50 | ```yml [compose.yml] 51 | services: 52 | php: 53 | image: serversideup/php:cli 54 | volumes: 55 | - ./:/var/www/html 56 | ``` 57 | 58 | Once you have your `compose.yml` file set, you can use the `docker compose` cli to start a container with your configuration. 59 | 60 | ```bash [Terminal] 61 | docker compose run -it php bash 62 | ``` 63 | 64 | Or you can pass commands directly to the container without starting a shell. 65 | 66 | ::note 67 | Don't get confused. `php` is in this command twice because it's the name of the service and the command to run inside the container. If this is too confusing, you can set your service name to something else like `app` in your `compose.yml` file. 68 | :: 69 | 70 | ```bash [Terminal] 71 | docker compose run php php my-script.php 72 | ``` 73 | 74 | ### Further Customization 75 | If you need to customize the container further, reference the docs below: 76 | 77 | - [Environment Variable Specifications](/docs/reference/environment-variable-specification) - See which environment variables are available to customize common PHP settings. 78 | - [Command Reference](/docs/reference/command-reference) - See which commands are available to run inside the container. 79 | -------------------------------------------------------------------------------- /docs/app/components/Testimonials.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/s6/usr/local/bin/docker-php-serversideup-s6-init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | ############################################### 5 | # Usage: docker-php-serversideup-s6-init 6 | ############################################### 7 | # This script is used to take scripts from "/etc/entrypoint.d" and move them 8 | # to the S6 Overlay structure. 9 | 10 | S6_HOME="/etc/s6-overlay" 11 | ENTRYPOINT_DIR="/etc/entrypoint.d" 12 | 13 | # Sanity checks 14 | if [ ! -d "$ENTRYPOINT_DIR" ]; then 15 | echo "Error: $ENTRYPOINT_DIR directory not found" 16 | exit 1 17 | fi 18 | 19 | if [ ! -d "$S6_HOME" ]; then 20 | echo "Error: S6_HOME directory ($S6_HOME) not found" 21 | exit 1 22 | fi 23 | 24 | # Check for root privileges 25 | if [ "$(id -u)" -ne 0 ]; then 26 | echo "$(basename "$0"): This script must be run as root within the container. Be sure to set \"USER root\" in your Dockerfile before running this script." 27 | exit 1 28 | fi 29 | 30 | for file in "$ENTRYPOINT_DIR"/*.sh; do 31 | [ -e "$file" ] || continue # Skip if no files match 32 | 33 | # Get the base name of the file 34 | script_name=$(basename "$file" .sh) 35 | 36 | # Proceed only if the script does not exist 37 | if [ ! -e "${S6_HOME}/scripts/${script_name}" ]; then 38 | # Create the service directory for that file 39 | mkdir -p "${S6_HOME}/s6-rc.d/${script_name}" 40 | 41 | # Set service type to "oneshot" 42 | echo "oneshot" > "${S6_HOME}/s6-rc.d/${script_name}/type" 43 | 44 | # Set the "up" script 45 | echo "${S6_HOME}/scripts/${script_name}" > "${S6_HOME}/s6-rc.d/${script_name}/up" 46 | 47 | # Place empty file in contents.d 48 | touch "${S6_HOME}/s6-rc.d/user/contents.d/${script_name}" 49 | 50 | # Ensure the ${S6_HOME}/scripts/ directory exists 51 | mkdir -p "${S6_HOME}/scripts" 52 | 53 | # Move the script to the S6 Overlay scripts directory 54 | mv "${file}" "${S6_HOME}/scripts/${script_name}" 55 | 56 | # Ensure the script has the correct file header for S6 57 | sed -i '1s%^#!/bin/sh$%#!/command/with-contenv sh%' "${S6_HOME}/scripts/${script_name}" 58 | 59 | # Find the script that should be the dependency based on alphabetical order 60 | previous_script_name=$(find "${S6_HOME}/s6-rc.d/" -maxdepth 1 -type d -name '[0-9]*' | \ 61 | sort -V | \ 62 | grep -B1 "${script_name}" | \ 63 | head -n 1 | \ 64 | xargs basename) 65 | 66 | # Check if the previous script is not the current script and set as dependency 67 | if [ "$previous_script_name" != "$script_name" ] && [ -n "$previous_script_name" ]; then 68 | dependencies_file="${S6_HOME}/s6-rc.d/${script_name}/dependencies" 69 | touch "$dependencies_file" 70 | echo "$previous_script_name" >> "$dependencies_file" 71 | chmod 644 "$dependencies_file" 72 | fi 73 | 74 | # Set the previous file for the next loop 75 | previous_script_name="$script_name" 76 | else 77 | echo "Skipping ${script_name} because it already exists at ${S6_HOME}/scripts/${script_name}" 78 | fi 79 | 80 | done -------------------------------------------------------------------------------- /src/variations/fpm-apache/etc/apache2/conf-available/security.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Disable access to the entire file system except for the directories that 3 | # are explicitly allowed later. 4 | # 5 | # This currently breaks the configurations that come with some web application 6 | # Debian packages. 7 | # 8 | # 9 | # AllowOverride None 10 | # Require all denied 11 | # 12 | 13 | 14 | # Changing the following options will not really affect the security of the 15 | # server, but might make attacks slightly more difficult in some cases. 16 | 17 | # 18 | # ServerTokens 19 | # This directive configures what you return as the Server HTTP response 20 | # Header. The default is 'Full' which sends information about the OS-Type 21 | # and compiled in modules. 22 | # Set to one of: Full | OS | Minimal | Minor | Major | Prod 23 | # where Full conveys the most information, and Prod the least. 24 | #ServerTokens Minimal 25 | # ServerTokens OS 26 | # #ServerTokens Full 27 | ServerTokens Prod 28 | 29 | # 30 | # Optionally add a line containing the server version and virtual host 31 | # name to server-generated pages (internal error documents, FTP directory 32 | # listings, mod_status and mod_info output etc., but not CGI generated 33 | # documents or custom error documents). 34 | # Set to "EMail" to also include a mailto: link to the ServerAdmin. 35 | # Set to one of: On | Off | EMail 36 | ServerSignature Off 37 | # ServerSignature On 38 | 39 | # 40 | # Allow TRACE method 41 | # 42 | # Set to "extended" to also reflect the request body (only for testing and 43 | # diagnostic purposes). 44 | # 45 | # Set to one of: On | Off | extended 46 | TraceEnable Off 47 | #TraceEnable On 48 | 49 | # 50 | # Forbid access to version control directories 51 | # 52 | # If you use version control systems in your document root, you should 53 | # probably deny access to their directories. For example, for subversion: 54 | # 55 | 56 | Require all denied 57 | 58 | 59 | # Prevent Apache from serving Gitlab files 60 | 61 | Require all denied 62 | 63 | 64 | # Disable XML-RPC on all wordpress sites 65 | 66 | Require all denied 67 | # allow from xxx.xxx.xxx.xxx 68 | 69 | 70 | # 71 | # Setting this header will prevent MSIE from interpreting files as something 72 | # else than declared by the content type in the HTTP headers. 73 | # Requires mod_headers to be enabled. 74 | # 75 | Header always set X-Content-Type-Options: "nosniff" 76 | 77 | # 78 | # Setting this header will prevent other sites from embedding pages from this 79 | # site as frames. This defends against clickjacking attacks. 80 | # Requires mod_headers to be enabled. 81 | # 82 | Header always set X-Frame-Options: "sameorigin" 83 | 84 | # 85 | # Referrer policy 86 | # 87 | Header always set Referrer-Policy "no-referrer-when-downgrade" 88 | 89 | # 90 | # Content Security Policy 91 | # UPDATE - September 2020: Commenting this out until we grasp better security requirements 92 | # 93 | #Header always set Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" 94 | 95 | # 96 | # Strict-Transport-Security Policy (set HSTS) 97 | # 98 | Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains" -------------------------------------------------------------------------------- /docs/app/components/AppLogo.vue: -------------------------------------------------------------------------------- 1 | 41 | -------------------------------------------------------------------------------- /docs/app/assets/icons/services/kubernetes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/etc/entrypoint.d/1-log-output-level.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | script_name="log-output-level" 3 | 4 | if [ "$DISABLE_DEFAULT_CONFIG" = true ]; then 5 | if [ "$LOG_OUTPUT_LEVEL" = "debug" ]; then 6 | echo "👉 $script_name: DISABLE_DEFAULT_CONFIG does not equal \"false\", so debug mode will NOT be automatically set." 7 | fi 8 | exit 0 # Exit if DISABLE_DEFAULT_CONFIG is true 9 | fi 10 | 11 | ####################################### 12 | # Functions 13 | ####################################### 14 | 15 | fpm_is_installed (){ 16 | if [ -d "/usr/local/etc/php-fpm.d" ]; then 17 | return 0 18 | else 19 | return 1 20 | fi 21 | } 22 | 23 | set_php_ini (){ 24 | php_ini_setting=$1 25 | php_ini_value=$2 26 | php_ini_debug_file="$PHP_INI_DIR/conf.d/zzz-serversideup-docker-php-debug.ini" 27 | php_fpm_debug_conf_file="/usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf" 28 | 29 | echo "$php_ini_setting = $php_ini_value" >> "$php_ini_debug_file" 30 | echo "ℹ️ NOTICE ($script_name): INI - $php_ini_setting has been set to \"$php_ini_value\"." 31 | 32 | # Check for PHP-FPM 33 | if fpm_is_installed; then 34 | echo "php_admin_value[$php_ini_setting] = $php_ini_value" >> "$php_fpm_debug_conf_file" 35 | echo "ℹ️ NOTICE ($script_name): FPM - $php_ini_setting has been set to \"$php_ini_value\"" 36 | fi 37 | } 38 | 39 | set_fpm_log_level (){ 40 | if ! fpm_is_installed; then 41 | return 0 42 | fi 43 | 44 | fpm_log_level=$1 45 | 46 | # Create a temporary file then move it, instead of requiring permissions to write to /usr/local/etc/ 47 | tmp_file=$(mktemp /tmp/php-fpm.conf.XXXXXX) 48 | sed "/\[global\]/a log_level = $fpm_log_level" /usr/local/etc/php-fpm.conf > "$tmp_file" 49 | cat "$tmp_file" > /usr/local/etc/php-fpm.conf 50 | rm "$tmp_file" 51 | echo "ℹ️ NOTICE ($script_name): FPM - log_level has been set to \"$fpm_log_level\"" 52 | 53 | if [ "$fpm_log_level" = "debug" ]; then 54 | echo "access.log = /proc/self/fd/2" >> /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf 55 | echo "access.format = \"fpm: %R - %u %t \"%m %r%Q%q\" %s duration=%{milliseconds}dms memory=%Mk cpu=%C%% pid=%p script=%f\"" >> /usr/local/etc/php-fpm.d/zzz-docker-php-serversideup-fpm-debug.conf 56 | echo "ℹ️ NOTICE ($script_name): FPM - access.log has been set to \"STDERR\"" 57 | fi 58 | } 59 | 60 | ####################################### 61 | # Main (if default config is enabled) 62 | ####################################### 63 | 64 | case "$LOG_OUTPUT_LEVEL" in 65 | debug) 66 | set_php_ini display_errors On 67 | set_php_ini display_startup_errors On 68 | set_php_ini error_reporting "32767" # E_ALL 69 | set_fpm_log_level debug 70 | ;; 71 | info) 72 | set_fpm_log_level notice 73 | ;; 74 | notice) 75 | set_fpm_log_level notice 76 | ;; 77 | warn) 78 | : # Do nothing 79 | ;; 80 | error) 81 | set_fpm_log_level error 82 | ;; 83 | crit) 84 | set_fpm_log_level alert 85 | ;; 86 | alert) 87 | set_fpm_log_level alert 88 | ;; 89 | emerg) 90 | set_fpm_log_level alert 91 | ;; 92 | *) 93 | echo "❌ ERROR ($script_name): LOG_OUTPUT_LEVEL is not set to a valid value. Please set it to one of the following: debug, info, notice, warn, error, crit, alert, emerg." 94 | return 1 95 | ;; 96 | esac 97 | -------------------------------------------------------------------------------- /docs/public/images/logos/docker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/content/docs/7.troubleshooting/1.common-issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Common Issues 3 | description: 'Solutions to frequently encountered problems when using serversideup/php Docker images.' 4 | --- 5 | 6 | ## Overview 7 | 8 | This guide covers the most common issues users encounter and their solutions. If you don't find your issue here, check out our [Getting Help](/docs/troubleshooting/getting-help) guide for community and professional support options. 9 | 10 | ## Permission Issues 11 | 12 | Permission problems are one of the most common issues when working with Docker containers. They typically manifest as "Permission denied" errors when trying to write files. 13 | 14 | ### Understanding the Problem 15 | 16 | By default, these images run as a non-root user (`www-data`) for security. When your host machine uses a different user ID (UID) or group ID (GID), file permission conflicts can occur. 17 | 18 | If your command is failing during build, then you likely need to switch to root to perform root tasks. 19 | 20 | ```dockerfile [Dockerfile] 21 | FROM serversideup/php:8.5-fpm-nginx 22 | 23 | USER root 24 | 25 | # Install system packages 26 | RUN install-php-extensions intl bcmath 27 | 28 | # Switch back to www-data 29 | USER www-data 30 | ``` 31 | 32 | ::warning 33 | Always switch back to a non-root user after completing privileged operations. Running containers as root is a security risk. 34 | :: 35 | 36 | If your container is failing to run during runtime, then you may have a more advanced permissions issue. See our guide on understanding file permissions. 37 | 38 | :u-button{to="/docs/guide/understanding-file-permissions" label="Understanding File Permissions" aria-label="Understanding File Permissions" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} 39 | 40 | ## Port Already in Use 41 | 42 | ### Error Message 43 | 44 | ``` 45 | Error starting xyz service: listen tcp 0.0.0.0:80: bind: address already in use 46 | ``` 47 | 48 | ### Solution 49 | 50 | Another process is using the port. Find and stop it, or use a different port: 51 | 52 | **Find what's using the port:** 53 | 54 | ```bash 55 | # On Linux/macOS 56 | sudo lsof -i :80 57 | 58 | # On Windows 59 | netstat -ano | findstr :80 60 | ``` 61 | 62 | **If you must use a different port:** 63 | 64 | ```yaml [compose.yml] {4-6} 65 | services: 66 | php: 67 | image: serversideup/php:8.5-fpm-nginx 68 | ports: 69 | - "8080:8080" # Use port 8080 instead 70 | - "8443:8443" 71 | ``` 72 | 73 | Access your application at `http://localhost:8080`. 74 | 75 | ## Getting More Help 76 | 77 | If your issue isn't covered here: 78 | 79 | 1. **Search GitHub Discussions** - Someone may have encountered the same problem 80 | 2. **Check the Documentation** - Review guides specific to your setup 81 | 3. **Ask the Community** - Post in GitHub Discussions or Discord 82 | 4. **Review Container Logs** - Most issues show helpful error messages 83 | 84 | :u-button{to="/docs/troubleshooting/getting-help" label="View All Support Options" aria-label="View All Support Options" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} 85 | 86 | ::tip 87 | When asking for help, always include your image version, `compose.yml`, relevant error messages, and what you've already tried. This helps others help you faster! 88 | :: 89 | -------------------------------------------------------------------------------- /docs/app/components/LandingSignup.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | -------------------------------------------------------------------------------- /scripts/conf/php-versions-base-config.yml: -------------------------------------------------------------------------------- 1 | php_variations: 2 | - name: cli 3 | default: true 4 | - name: fpm 5 | - name: fpm-apache 6 | supported_os: # Open a discussion on serversideup/php if you want to see Alpine support for fpm-apache (https://github.com/serversideup/docker-php/discussions/66) 7 | - bullseye 8 | - bookworm 9 | - trixie 10 | - name: fpm-nginx 11 | - name: frankenphp 12 | excluded_minor_versions: 13 | - "7.4" 14 | - "8.0" 15 | - "8.1" 16 | - "8.2" 17 | 18 | php_versions: 19 | - major: "7" 20 | minor_versions: 21 | - minor: "7.4" 22 | base_os: 23 | - name: alpine3.16 24 | - name: bullseye 25 | patch_versions: 26 | - 7.4.33 27 | - major: "8" 28 | minor_versions: 29 | - minor: "8.0" 30 | base_os: 31 | - name: alpine3.16 32 | - name: bullseye 33 | patch_versions: 34 | - 8.0.30 35 | - minor: "8.1" 36 | base_os: 37 | - name: alpine3.21 38 | - name: bookworm 39 | - name: trixie 40 | patch_versions: 41 | # - 8.1.28 # Pull latest from Official PHP source 42 | - minor: "8.2" 43 | base_os: 44 | - name: alpine3.21 45 | - name: alpine3.22 46 | - name: bookworm 47 | - name: trixie 48 | patch_versions: 49 | # - 8.2.18 # Pull latest from Official PHP source 50 | - minor: "8.3" 51 | base_os: 52 | - name: alpine3.21 53 | - name: alpine3.22 54 | - name: bookworm 55 | - name: trixie 56 | patch_versions: 57 | # - 8.3.6 # Pull latest from Official PHP source 58 | - minor: "8.4" 59 | base_os: 60 | - name: alpine3.21 61 | - name: alpine3.22 62 | - name: bookworm 63 | - name: trixie 64 | patch_versions: 65 | # - 8.4.1 # Pull latest from Official PHP source 66 | - minor: "8.5" 67 | base_os: 68 | - name: alpine3.21 69 | - name: alpine3.22 70 | - name: bookworm 71 | - name: trixie 72 | patch_versions: 73 | # - 8.5.0 # Pull latest from Official PHP source 74 | 75 | operating_systems: 76 | - family: alpine 77 | versions: 78 | - name: "Alpine 3.16" 79 | version: alpine3.16 80 | number: 3.16 81 | nginx_version: 1.26.1-r2 82 | - name: "Alpine 3.17" 83 | version: alpine3.17 84 | number: 3.17 85 | nginx_version: 1.26.2-r1 86 | - name: "Alpine 3.18" 87 | version: alpine3.18 88 | number: 3.18 89 | nginx_version: 1.28.0-r1 90 | - name: "Alpine 3.19" 91 | version: alpine3.19 92 | number: 3.19 93 | nginx_version: 1.28.0-r1 94 | - name: "Alpine 3.20" 95 | version: alpine3.20 96 | number: 3.20 97 | nginx_version: 1.28.0-r1 98 | - name: "Alpine 3.21" 99 | version: alpine3.21 100 | number: 3.21 101 | nginx_version: 1.28.0-r1 102 | - name: "Alpine 3.22" 103 | version: alpine3.22 104 | number: 3.22 105 | nginx_version: 1.28.0-r1 106 | - family: debian 107 | default: true 108 | versions: 109 | - name: "Debian Bullseye" 110 | version: bullseye 111 | number: 11 112 | nginx_version: 1.28.0-1~bullseye 113 | - name: "Debian Bookworm" 114 | version: bookworm 115 | number: 12 116 | nginx_version: 1.28.0-1~bookworm 117 | - name: "Debian Trixie" 118 | version: trixie 119 | number: 13 120 | nginx_version: 1.28.0-1~trixie 121 | -------------------------------------------------------------------------------- /docs/content/docs/2.image-variations/unit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Unit (Deprecated) 3 | description: 'NGINX Unit has been archived. This guide helps you understand what happened and how to migrate to actively maintained alternatives.' 4 | --- 5 | 6 | ## NGINX Unit Has Been Archived 7 | 8 | In October 2025, NGINX officially archived the NGINX Unit project and stopped all maintenance. If you're seeing this page, you're likely using our Unit-based images and wondering what to do next. 9 | 10 | ::caution{to="https://github.com/nginx/unit?tab=readme-ov-file#nginx-unit" target="_blank"} 11 | **The Unit variation is deprecated and has been removed from our project.** [View official NGINX announcement →](https://github.com/nginx/unit?tab=readme-ov-file#nginx-unit){target="_blank"} 12 | :: 13 | 14 | **The good news:** Your application will continue to work, and you have time to plan your migration. Below, we'll answer your most important questions and guide you through your options. 15 | 16 | ## Common Questions 17 | 18 | ### Will my application stop working immediately? 19 | 20 | No. Your existing containers will continue to run without interruption. However: 21 | 22 | - **No security updates:** NGINX Unit will not receive security patches 23 | - **No bug fixes:** Any issues with Unit itself won't be resolved 24 | - **No new PHP versions:** Unit may not support future PHP releases 25 | 26 | You should prioritize your migration, but you're not in a downtime situation. 27 | 28 | ### What should I migrate to? 29 | 30 | We recommend **FrankenPHP** as the best alternative because it offers: 31 | 32 | - **Single-process architecture** (similar to Unit's design) 33 | - **Built-in HTTP/2 and HTTP/3 support** 34 | - **Active development** by the PHP community 35 | - **Laravel Octane support** for enhanced performance 36 | - **Better performance** than traditional PHP-FPM setups 37 | 38 | :u-button{to="/docs/image-variations/frankenphp" label="Learn about FrankenPHP" aria-label="Learn about FrankenPHP variation" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} 39 | 40 | ### What if FrankenPHP doesn't work for me? 41 | 42 | You have other proven options: 43 | 44 | - **[FPM + NGINX](/docs/image-variations/fpm-nginx)** - Traditional, highly scalable setup (recommended for most production apps) 45 | - **[FPM + Apache](/docs/image-variations/fpm-apache)** - If you need `.htaccess` support or prefer Apache 46 | - **[CLI](/docs/image-variations/cli)** - For queue workers, scheduled tasks, and CLI-only workloads 47 | 48 | All of these variations are actively maintained and production-ready. 49 | 50 | ### How urgent is this migration? 51 | 52 | **Timeline:** 53 | - **Now:** You can still pull `serversideup/php:unit` images, but they will not receive any future feature or security updates. 54 | 55 | **Recommendation:** Start planning your migration now. Don't rush, but don't delay indefinitely. 56 | 57 | ### Where can I get help? 58 | 59 | We're here to support you through this transition: 60 | 61 | - **Community Support:** [Post on our forum](https://serversideup.net/php/community) or [join our Discord](https://serversideup.net/discord) for migration questions 62 | - **Migration Assistance:** Ask questions specific to your setup 63 | - **Documentation:** Follow our comprehensive guides for each variation 64 | 65 | ## Need More Information? 66 | 67 | Explore the documentation for your chosen variation: 68 | 69 | - [FrankenPHP Documentation](/docs/image-variations/frankenphp) 70 | - [FPM + NGINX Documentation](/docs/image-variations/fpm-nginx) 71 | - [FPM + Apache Documentation](/docs/image-variations/fpm-apache) 72 | 73 | Each variation includes detailed configuration examples, performance tuning tips, and deployment guides. -------------------------------------------------------------------------------- /src/variations/cli/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_OS_VERSION='bookworm' 2 | ARG PHP_VERSION='8.5' 3 | ARG PHP_VARIATION='cli' 4 | 5 | ########## 6 | # CLI: Main Image 7 | ########## 8 | FROM php:${PHP_VERSION}-${PHP_VARIATION}-${BASE_OS_VERSION} 9 | 10 | ARG DEPENDENCY_PACKAGES_ALPINE='shadow' 11 | ARG DEPENDENCY_PACKAGES_DEBIAN='procps zip' 12 | ARG DEPENDENCY_PHP_EXTENSIONS='opcache pcntl pdo_mysql pdo_pgsql redis zip' 13 | ARG REPOSITORY_BUILD_VERSION='dev' 14 | 15 | LABEL org.opencontainers.image.title="serversideup/php (cli)" \ 16 | org.opencontainers.image.description="Supercharge your PHP experience. Based off the official PHP images, serversideup/php includes pre-configured PHP extensions and settings for enhanced performance and security. Optimized for Laravel and WordPress." \ 17 | org.opencontainers.image.url="https://serversideup.net/open-source/docker-php/" \ 18 | org.opencontainers.image.source="https://github.com/serversideup/docker-php" \ 19 | org.opencontainers.image.documentation="https://serversideup.net/open-source/docker-php/docs/" \ 20 | org.opencontainers.image.vendor="ServerSideUp" \ 21 | org.opencontainers.image.authors="Jay Rogers (@jaydrogers)" \ 22 | org.opencontainers.image.version="${REPOSITORY_BUILD_VERSION}" \ 23 | org.opencontainers.image.licenses="GPL-3.0-or-later" 24 | 25 | ENV APP_BASE_DIR=/var/www/html \ 26 | COMPOSER_ALLOW_SUPERUSER=1 \ 27 | COMPOSER_HOME=/composer \ 28 | COMPOSER_MAX_PARALLEL_HTTP=24 \ 29 | DISABLE_DEFAULT_CONFIG=false \ 30 | LOG_OUTPUT_LEVEL=warn \ 31 | PHP_DATE_TIMEZONE="UTC" \ 32 | PHP_DISPLAY_ERRORS=Off \ 33 | PHP_DISPLAY_STARTUP_ERRORS=Off \ 34 | PHP_ERROR_LOG="/dev/stderr" \ 35 | PHP_ERROR_REPORTING="22527" \ 36 | PHP_MAX_EXECUTION_TIME="99" \ 37 | PHP_MAX_INPUT_TIME="-1" \ 38 | PHP_MAX_INPUT_VARS="1000" \ 39 | PHP_MEMORY_LIMIT="256M" \ 40 | PHP_OPCACHE_ENABLE="0" \ 41 | PHP_OPCACHE_ENABLE_FILE_OVERRIDE="0" \ 42 | PHP_OPCACHE_FORCE_RESTART_TIMEOUT="180" \ 43 | PHP_OPCACHE_INTERNED_STRINGS_BUFFER="8" \ 44 | PHP_OPCACHE_JIT="off" \ 45 | PHP_OPCACHE_JIT_BUFFER_SIZE="0" \ 46 | PHP_OPCACHE_MAX_ACCELERATED_FILES="10000" \ 47 | PHP_OPCACHE_MEMORY_CONSUMPTION="128" \ 48 | PHP_OPCACHE_REVALIDATE_FREQ="2" \ 49 | PHP_OPCACHE_SAVE_COMMENTS="1" \ 50 | PHP_OPCACHE_VALIDATE_TIMESTAMPS="1" \ 51 | PHP_OPEN_BASEDIR="" \ 52 | PHP_POST_MAX_SIZE="100M" \ 53 | PHP_REALPATH_CACHE_TTL="120" \ 54 | PHP_SESSION_COOKIE_SECURE=false \ 55 | PHP_UPLOAD_MAX_FILE_SIZE="100M" \ 56 | PHP_ZEND_DETECT_UNICODE="" \ 57 | PHP_ZEND_MULTIBYTE="Off" \ 58 | SHOW_WELCOME_MESSAGE=true 59 | 60 | # copy our scripts 61 | COPY --chmod=755 src/common/ / 62 | 63 | # install pecl extensions & dependencies 64 | RUN docker-php-serversideup-dep-install-alpine "${DEPENDENCY_PACKAGES_ALPINE}" && \ 65 | docker-php-serversideup-dep-install-debian "${DEPENDENCY_PACKAGES_DEBIAN}" && \ 66 | docker-php-serversideup-install-php-ext-installer && \ 67 | \ 68 | # Ensure /var/www/ has the correct permissions 69 | chown -R www-data:www-data /var/www && \ 70 | chmod -R 755 /var/www && \ 71 | \ 72 | # Set the image version 73 | echo "${REPOSITORY_BUILD_VERSION}" > /etc/serversideup-php-version && \ 74 | \ 75 | # Make composer cache directory 76 | mkdir -p "${COMPOSER_HOME}" && \ 77 | chown -R www-data:www-data "${COMPOSER_HOME}" && \ 78 | \ 79 | # Install default PHP extensions 80 | install-php-extensions ${DEPENDENCY_PHP_EXTENSIONS} 81 | 82 | # install composer from Composer's official Docker image 83 | COPY --from=composer:2 /usr/bin/composer /usr/bin/composer 84 | 85 | RUN docker-php-serversideup-set-file-permissions --owner www-data:www-data --service cli 86 | 87 | WORKDIR ${APP_BASE_DIR} 88 | 89 | USER www-data 90 | 91 | ENTRYPOINT ["docker-php-serversideup-entrypoint"] 92 | -------------------------------------------------------------------------------- /docs/content/docs/1.getting-started/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | description: 'Production-ready PHP Docker images built on official PHP. Optimized for security, performance, and developer experience.' 4 | --- 5 | 6 | ::hero-video 7 | --- 8 | src: https://docker-php-public-assets.serversideup.net/docker-demo.mp4 9 | --- 10 | :: 11 | 12 | ## What's "serversideup/php" 13 | 14 | :badges 15 | 16 | **serversideup/php** takes the official PHP Docker images and adds everything you need for production: better security, performance optimizations, and a developer experience that just works. 17 | 18 | ## These images are very different from other PHP Docker Images 19 | ::u-page-grid 20 | :::u-page-card 21 | --- 22 | icon: i-features-rocket-square 23 | orientation: vertical 24 | --- 25 | #title 26 | Production Ready 27 | 28 | #description 29 | Built to be performant and secure to exist on the wild web. 30 | ::: 31 | 32 | :::u-page-card 33 | --- 34 | icon: i-features-heart-square 35 | orientation: vertical 36 | --- 37 | #title 38 | Native Health Checks 39 | 40 | #description 41 | Be 100% confident your application is actually running. 42 | ::: 43 | 44 | :::u-page-card 45 | --- 46 | icon: i-features-lightning-square 47 | orientation: vertical 48 | --- 49 | #title 50 | High Performance 51 | 52 | #description 53 | Get the easiest experience for fine tuning performance. 54 | ::: 55 | 56 | :::u-page-card 57 | --- 58 | icon: i-features-stars-square 59 | orientation: vertical 60 | --- 61 | #title 62 | Customizable & Flexible 63 | 64 | #description 65 | Environment variables make customizations a breeze. 66 | ::: 67 | 68 | :::u-page-card 69 | --- 70 | icon: i-features-cloudflare-square 71 | orientation: vertical 72 | --- 73 | #title 74 | Native CloudFlare Support 75 | 76 | #description 77 | Get real IP addresses from visitors from trusted proxies. 78 | ::: 79 | 80 | :::u-page-card 81 | --- 82 | icon: i-features-php-square 83 | orientation: vertical 84 | --- 85 | #title 86 | Based on official PHP 87 | 88 | #description 89 | Upgrade from the official PHP docker images with confidence. 90 | ::: 91 | 92 | :::u-page-card 93 | --- 94 | icon: i-features-frankenphp-square 95 | orientation: vertical 96 | --- 97 | #title 98 | FrankenPHP 99 | 100 | #description 101 | Ditch FPM for a modern way of running PHP. Designed for containers from the ground up. 102 | ::: 103 | 104 | :::u-page-card 105 | --- 106 | icon: i-features-logging-square 107 | orientation: vertical 108 | --- 109 | #title 110 | Unified Logging 111 | 112 | #description 113 | All logs are directed to STDOUT & STDERR for centralized output. 114 | ::: 115 | 116 | :::u-page-card 117 | --- 118 | icon: i-features-heartbeat-square 119 | orientation: vertical 120 | --- 121 | #title 122 | FPM + S6 Overlay 123 | 124 | #description 125 | Our FPM-Apache & FPM-NGINX images use this intelligent init system. 126 | ::: 127 | :: 128 | 129 |
130 | 131 | :u-button{to="/docs/getting-started/these-images-vs-others" label="See all advantages" aria-label="Read more about the advantages" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} 132 | 133 | ## Ready to get started? 134 | 135 | Ship PHP applications faster with a production-ready setup that includes everything you need out of the box. 136 | 137 | :u-button{to="/docs/getting-started/installation" label="Installation" aria-label="Installation" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} -------------------------------------------------------------------------------- /docs/app/assets/icons/hosts/hetzner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/content/docs/3.framework-guides/1.laravel/4.reverb.md: -------------------------------------------------------------------------------- 1 | --- 2 | head.title: 'Laravel Reverb with Docker - Docker PHP - Server Side Up' 3 | description: 'Learn how to configure Laravel Reverb with Docker.' 4 | layout: docs 5 | title: Reverb 6 | --- 7 | 8 | ## Laravel Reverb with Docker 9 | Run Laravel Reverb by passing the Artisan command as the container's command. Reverb is Laravel's WebSocket server for real-time communication. 10 | 11 | ::tip{to="https://getspin.pro/docs/services/laravel-scheduler" target="_blank"} 12 | Want to skip the setup? [Spin Pro](https://getspin.pro/docs/services/laravel-scheduler){target="_blank"} handles Laravel Reverb on your VPS with Docker and zero-downtime deployments—all configured for you. 13 | :: 14 | 15 | ::note 16 | Before using Reverb with Docker, follow the [Laravel Reverb setup instructions](https://laravel.com/docs/12.x/reverb) to install and configure the Reverb package in your Laravel application. 17 | :: 18 | 19 | ## Docker Compose example 20 | 21 | This example runs Reverb as a separate container using the same image as your web service. Reverb requires a reverse proxy (like Traefik) to route WebSocket traffic correctly. 22 | 23 | **Key points:** 24 | - Use the same image for both your web and Reverb services 25 | - Reverb runs on a different port than your web server (8000 vs 8080) 26 | - Set `SIGTERM` as the stop signal for graceful shutdown (especially for `fpm-apache` and `fpm-nginx`) 27 | - Include a health check to monitor Reverb status 28 | - Configure a reverse proxy to route traffic to the correct container 29 | ```yml [compose.yml] 30 | services: 31 | php: 32 | image: my/laravel-app 33 | labels: 34 | - "traefik.enable=true" 35 | - "traefik.http.routers.laravel.tls=true" 36 | - "traefik.http.routers.laravel.entrypoints=websecure" 37 | - "traefik.http.routers.laravel.rule=Host(`https://app.example.com`)" 38 | - "traefik.http.services.laravel.loadbalancer.server.port=8080" 39 | - "traefik.http.services.laravel.loadbalancer.server.scheme=http" 40 | 41 | reverb: 42 | image: my/laravel-app 43 | command: ["php", "/var/www/html/artisan", "reverb:start", "--port=8000"] 44 | stop_signal: SIGTERM 45 | healthcheck: 46 | test: ["CMD", "healthcheck-reverb"] 47 | start_period: 10s 48 | labels: 49 | - "traefik.enable=true" 50 | - "traefik.http.routers.reverb.tls=true" 51 | - "traefik.http.routers.reverb.entrypoints=websecure" 52 | - "traefik.http.routers.reverb.rule=Host(`https://reverb.example.com`)" 53 | - "traefik.http.services.reverb.loadbalancer.server.port=8000" 54 | - "traefik.http.services.reverb.loadbalancer.server.scheme=http" 55 | ``` 56 | 57 | ## Environment variable configuration 58 | 59 | ::warning 60 | Don't confuse **CLIENT** variables with **SERVER** variables. The `REVERB_SERVER_*` variables configure the Reverb daemon itself, while `REVERB_*` variables tell your frontend clients how to connect. 61 | :: 62 | 63 | Configure these environment variables in your Laravel application: 64 | 65 | | **Laravel ENV Variable** | **Description** | **Value (matching example above)** | 66 | | ------------------------- | --------------- | ---------------------------------- | 67 | | `REVERB_HOST` | The hostname the **CLIENT** will connect to | `reverb.example.com` | 68 | | `REVERB_PORT` | The port the **CLIENT** will connect to | `443` | 69 | | `REVERB_SCHEME` | The scheme the **CLIENT** will connect to | `https` | 70 | 71 | ## Advanced configuration 72 | 73 | ::tip 74 | **Multiple processes in one container:** If you're running `fpm-nginx` or `fpm-apache` and you'd like to have everything in a single container, you can [write your own S6 Overlay service script](https://github.com/just-containers/s6-overlay/tree/master#writing-a-service-script){target="_blank"} to properly manage multiple processes in a single container. Learn more about about this in our [Using S6 Overlay guide](/docs/guide/using-s6-overlay). 75 | :: -------------------------------------------------------------------------------- /docs/content/docs/5.guide/1.migrating-from-official-php-images.md: -------------------------------------------------------------------------------- 1 | --- 2 | head.title: 'Migrating from official PHP Docker images - Docker PHP - Server Side Up' 3 | description: 'Learn how easy it is to move from the official PHP docker images to serversideup/php.' 4 | layout: docs 5 | title: Migrating from official PHP images 6 | --- 7 | 8 | ::lead-p 9 | Migrating from the official PHP images to serversideup/php is easy because our images are based on the official PHP images. We just give you a "batteries included" experience that's ready for production. 10 | :: 11 | 12 | ## Key differences 13 | ::warning 14 | Because our images run as `www-data` by default, you may need to update file permissions for mounted volumes. 15 | :: 16 | 17 | | | **Official PHP Images** |**serversideup/php** | 18 | |-------------------------|-------------------------|---------------------| 19 | | Base Operating System | Debian, Alpine | Debian, Alpine | 20 | | PHP Compilation | PHP Source Code | PHP Source Code (based on official PHP images) | 21 | | Run PHP, pinned to the minor version | ✅ | ✅ | 22 | | Multi-arch support | ✅ | ✅ | 23 | | Init System | Docker CMD | Docker CMD or [S6-Overlay](https://github.com/just-containers/s6-overlay) | 24 | | Published Registry| DockerHub | [DockerHub](https://hub.docker.com/r/serversideup/php), [GitHub Packages](https://github.com/serversideup/docker-php/pkgs/container/php) | 25 | | Unprivileged by default | ❌ | ✅ | 26 | | Variable-first configuration | ❌ | ✅ | 27 | | Includes `composer` | ❌ | ✅ | 28 | | Includes [`install-php-extensions`](https://github.com/mlocati/docker-php-extension-installer) | ❌ | ✅ | 29 | | Production-Ready by default| ❌ | ✅ | 30 | | Built-in security optimizations | ❌ | ✅ | 31 | | Optimized for Laravel & WordPress| ❌ | ✅ | 32 | | NGINX + FPM variation| ❌ | ✅ | 33 | | FrankenPHP variation| ❌ | ✅ | 34 | | Native health checks | ❌ | ✅ | 35 | 36 | :u-button{to="/docs/getting-started/choosing-an-image" label="Learn more about choosing an image" aria-label="Learn more about choosing an image" size="md" color="primary" variant="outline" trailing-icon="i-lucide-arrow-right" class="font-bold ring ring-inset ring-blue-600 text-blue-600 hover:ring-blue-500 hover:text-blue-500"} 37 | 38 | ## Making the change 39 | Making the change will literally take you two seconds. 40 | 41 | ::steps{level="4"} 42 | 43 | #### Figure out which image you'd like to use 44 | Review our [choosing an image](/docs/getting-started/choosing-an-image) guide to help you decide which image you'd like to use. Also, make sure our [default configurations](/docs/getting-started/default-configurations) satisfy your requirements. 45 | 46 | #### Update your `Dockerfile` or `compose.yml` file 47 | :::tip 48 | We simply change `php:8.5-apache` to `serversideup/php:8.5-fpm-apache` 49 | ::: 50 | **Dockerfile** 51 | :::code-group 52 | ```dockerfile [ORIGINAL: Dockerfile]{1} 53 | FROM php:8.5-apache 54 | 55 | # Rest of your Dockerfile... 56 | ``` 57 | ```dockerfile [UPDATED: Dockerfile]{1} 58 | FROM serversideup/php:8.5-fpm-apache 59 | 60 | # Rest of your Dockerfile... 61 | ``` 62 | ::: 63 | 64 | **compose.yml** 65 | :::code-group 66 | ```yml [ORIGINAL: compose.yml]{3,5-6} 67 | services: 68 | php: 69 | image: php:8.5-apache 70 | ports: 71 | - 80:80 72 | - 443:443 73 | ``` 74 | ```yml [UPDATED: compose.yml]{3,5-6} 75 | services: 76 | php: 77 | image: serversideup/php:8.5-fpm-apache 78 | ports: 79 | - 80:8080 80 | - 443:8443 81 | ``` 82 | ::: 83 | 84 | #### Test your application 85 | Make sure to test your application to ensure it's working as expected. 86 | 87 | #### Deploy and enjoy! 88 | Making the change is that simple. 89 | 90 | :: 91 | 92 | ## Related resources 93 | If you need to customize the base image, review our guides below: 94 | 95 | - [Changing common PHP settings](/docs/customizing-the-image/changing-common-php-settings) 96 | - [Installing additional PHP extensions](/docs/customizing-the-image/installing-additional-php-extensions) 97 | - [Startup scripts](/docs/customizing-the-image/adding-your-own-start-up-scripts) -------------------------------------------------------------------------------- /docs/app/pages/[...slug].vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 84 | 85 | --------------------------------------------------------------------------------