This is a list of the docker images this repository creates:
| βοΈ Variation | π Version |
|---|---|
| cli | 7.4, 8.0 |
| fpm | 7.4, 8.0 |
| fpm-apache | 7.4, 8.0 |
| fpm-nginx | 7.4, 8.0 |
Simply use this image name pattern in any of your projects:
serversideup/php:{{version}}-{{variation-name}}For example... If I wanted to run PHP 8.0 with FPM + NGINX, I would use this image:
serversideup/php:8.0-fpm-nginxβ The image builds automatically run weekly (Tuesday at 0800 UTC) for latest security updates.
All images are built off of the official Ubuntu 20.04 docker image. We first build our CLI image, then our FPM, etc. Here is what this looks like:
We're taking the extra effort to open source as much as we can. Not only could this potentially help someone learn a little bit of Docker, but it makes it a heck of a lot easier for us to work with you on new open source ideas.
Majority of our knowledge came from Chris' course, Shipping Docker. If you have yet to discover his content, you will be very satisfied with every course he has to offer. He's a great human being and excellent educator.
This team has an excellent repository and millions of pulls per month. We really like how they structured their code.
These guys are absolute aces when it comes to Docker development. They are a great resource for tons of open source Docker images.
These images have a few key differences. These images are:
Our philosophy is: What you run in production is what you should be running in development.
You'd be shocked how many people create a Docker image and use it in the local development only. These images are designed with the intention of being deployed to the open and wild Internet.
We have a ton of helpful scripts and security settings configured for managing Laravel and WordPress.
We automatically look at your Laravel .env file and determine if theses tasks should be run.
If your APP_ENV != local(any environment other than local development), we will automatically run these repetative tasks for you every time the container spins up:
Database Migrations:
php /var/www/html/artisan migrate --forceStorage Linking:
php /var/www/html/artisan storage:linkWe need to run the schedule:work command from Laravel. Although the docs say "Running the scheduler locally", this is what we want in production. It will run the scheduler in the foreground and execute it every minute. You can configure your Laravel app for the exact time that a command should run through a scheduled task.
Task Scheduler Command:
php /var/www/html/artisan schedule:workExample Docker Compose File:
version: '3'
services:
php:
image: my/laravel-app
environment:
PHP_POOL_NAME: "my-app_php"
task:
image: my/laravel-app
command: "php /var/www/html/artisan schedule:work"
environment:
PHP_POOL_NAME: "my-app_task"All you need to do is pass the Laravel Queue command to the container and S6 will automatically monitor it for you.
Task Command:
php /var/www/html/artisan queue:work --tries=3Example Docker Compose File:
version: '3'
services:
php:
image: my/laravel-app
environment:
PHP_POOL_NAME: "my-app_php"
queue:
image: my/laravel-app
command: "php /var/www/html/artisan queue:work --tries=3"
environment:
PHP_POOL_NAME: "my-app_queue"By passing Laravel Horizon to our container, S6 will automatically monitor it.
Horizon Command:
php /var/www/html/artisan horizonExample Docker Compose File:
version: '3'
services:
php:
image: my/laravel-app
environment:
PHP_POOL_NAME: "my-app_php"
redis:
image: redis:6
command: "redis-server --appendonly yes --requirepass redispassword"
horizon:
image: my/laravel-app
command: "php /var/www/html/artisan horizon"
environment:
PHP_POOL_NAME: "my-app_horizon"- Hardening of Apache & NGINX included
- Disabling of XML-RPC
- Preventative access to sensitive version control or CI files
- Protection against other common attacks
See our Apache security.conf and NGINX security.conf for more detail.
π§ Based off of S6 Overlay
S6 Overlay is very helpful in managing a container's lifecycle that has multiple processes.
Wait... Isn't Docker supposed to be a "single process per container"? Yes, that's what it's like in a perfect world. Unfortunately PHP isn't like that. You need both a web server and a PHP-FPM server to see your files in order for your application to load.
We follow the S6 Overlay Philosophy on how we can still get a single, disposable, and repeatable image of our application out to our servers.
We like to customize our images on a per app basis using environment variables. Look below to see what variables are available and what their defaults are. You can easily override them in your own docker environments (see Docker's documentation).
| π Variable Name | π Description | βοΈ Used in variation | #οΈβ£ Default Value |
|---|---|---|---|
| PUID | User ID the webserver and PHP should run as. | all | 9999 |
| PGID | Group ID the webserver and PHP should run as. | all | 9999 |
| WEBUSER_HOME | BETA: You can change the home of the web user if needed. | all (except *-nginx) | /var/www/html |
| PHP_DATE_TIMEZONE | Control your timezone. (Official Docs) | fpm, fpm-nginx, fpm-apache |
"UTC" |
| PHP_DISPLAY_ERRORS | Show PHP errors on screen. (Official docs) | fpm, fpm-nginx, fpm-apache |
On |
| PHP_ERROR_REPORTING | Set PHP error reporting level. (Official docs) | fpm, fpm-nginx, fpm-apache |
"E_ALL & ~E_DEPRECATED & ~E_STRICT" |
| PHP_MAX_EXECUTION_TIME | Set the maximum time in seconds a script is allowed to run before it is terminated by the parser. (Official docs) | fpm, fpm-nginx, fpm-apache |
"99" |
| PHP_MEMORY_LIMIT | Set the maximum amount of memory in bytes that a script is allowed to allocate. (Official docs) | fpm, fpm-nginx, fpm-apache |
"256M" |
| PHP_PM_CONTROL | Choose how the process manager will control the number of child processes. (Official docs) | fpm, fpm-nginx, fpm-apache |
fpm: dynamic fpm-apache: ondemand fpm-nginx: ondemand |
| PHP_PM_MAX_CHILDREN | The number of child processes to be created when pm is set to static and the maximum number of child processes to be created when pm is set to dynamic. (Official docs) | fpm, fpm-nginx, fpm-apache |
"20" |
| PHP_PM_MAX_SPARE_SERVERS | The desired maximum number of idle server processes. Used only when pm is set to dynamic. (Official docs) | fpm, fpm-nginx, fpm-apache |
"3" |
| PHP_PM_MIN_SPARE_SERVERS | The desired minimum number of idle server processes. Used only when pm is set to dynamic. (Official docs) | fpm, fpm-nginx, fpm-apache |
"1" |
| PHP_PM_START_SERVERS | The number of child processes created on startup. Used only when pm is set to dynamic. (Official docs) | fpm, fpm-nginx, fpm-apache |
"2" |
| PHP_POOL_NAME | Set the name of your PHP-FPM pool (helpful when running multiple sites on a single server). | fpm, fpm-nginx, fpm-apache |
"www" |
| PHP_POST_MAX_SIZE | Sets max size of post data allowed. (Official docs) | fpm, fpm-nginx, fpm-apache |
"100M" |
| PHP_UPLOAD_MAX_FILE_SIZE | The maximum size of an uploaded file. (Official docs) | fpm, fpm-nginx, fpm-apache |
"100M" |
| RUN_LARAVEL_AUTOMATIONS | Automatically run the Laravel Automations (for non-local Laravel installs only) | fpm, fpm-nginx, fpm-apache |
"true" |
| MSMTP_RELAY_SERVER_HOSTNAME | Server that should relay emails for MSMTP. (Official docs) | fpm-nginx, fpm-apache |
"mailhog" π¨ IMPORTANT: Change this value if you want emails to work. (we set it to Mailhog so our staging sites do not send emails out) |
| MSMTP_RELAY_SERVER_PORT | Port the SMTP server is listening on. (Official docs) | fpm-nginx, fpm-apache |
"1025" (default port for Mailhog) |
| DEBUG_OUTPUT | Set this variable to true if you want to put PHP and your web server in debug mode. |
fpm-nginx, fpm-apache |
(undefined, false) |
| APACHE_DOCUMENT_ROOT | Sets the directory from which Apache will serve files. (Official docs) | fpm-apache | "/var/www/html" |
| APACHE_MAX_CONNECTIONS_PER_CHILD | Sets the limit on the number of connections that an individual child server process will handle.(Official docs) | fpm-apache | "0" |
| APACHE_MAX_REQUEST_WORKERS | Sets the limit on the number of simultaneous requests that will be served. (Official docs) | fpm-apache | "150" |
| APACHE_MAX_SPARE_THREADS | Maximum number of idle threads. (Official docs) | fpm-apache | "75" |
| APACHE_MIN_SPARE_THREADS | Minimum number of idle threads to handle request spikes. (Official docs) | fpm-apache | "10" |
| APACHE_RUN_GROUP | Set the username of what Apache should run as. | fpm-apache | "webgroup" |
| APACHE_RUN_USER | Set the username of what Apache should run as. | fpm-apache | "webuser" |
| APACHE_START_SERVERS | Sets the number of child server processes created on startup.(Official docs) | fpm-apache | "2" |
| APACHE_THREAD_LIMIT | Set the maximum configured value for ThreadsPerChild for the lifetime of the Apache httpd process. (Official docs) | fpm-apache | "64" |
| APACHE_THREADS_PER_CHILD | This directive sets the number of threads created by each child process. (Official docs) | fpm-apache | "25" |
You have a few options for running SSL. By default, we generate a self-signed certificate for simple local development. For production use, we recommend using Traefik or Caddy as a proxy to your actual container. This is what we do and it's really nice to use the automatic Let's Encrypt SSL management with these products.
If you really want you use your own provided certificate, you'll just need to use Docker Volumes and mount the /etc/ssl/web folder with these two files in that directory:
- /etc/ssl/web/ssl.crt
- /etc/ssl/web/ssl.key
You can see a bigger picture on how these images are used from Development to Production by viewing this video that shows a high level overview how we deploy "ROAST" which is a demo production app for our book.
Click the image below to view the video:
Since there are a lot of dependencies on these images, please understand that it can make it complicated on merging your pull request.
We'd love to have your help, but it might be best to explain your intentions first before contributing.
If you find a critical security flaw, please open an issue or learn more about our responsible disclosure policy.