update: Edited the shared example code the original did not define the top level volume and the name of the top level volume cannott be a dynamic yaml key. The new code refers to “data” in the services and the name is defined as a parameter of the top level volume definition. Thanks @meyay for pointing out the mistake.
Running separate containers for multiple services is a general rule which also applies to NginX and PHP FPM. Escpecially in poduction. You may want to be able to scale up and down just one service or run on multiple servers in a cluster. Not to mention different dependencies and security. Each container can access only files necessary to run that service in the container.
Yes, running applications in production can be tricky sometimes, that is why people can pay for professionals to do it right. If it is not worth it ti you, you and you don’t need multiple PHP and nginx instances independently, you can try to run it in a single container, but doing it would be tricky as well. You would need to run a process manager in the container like supervisor or s6-init.
If the application is written in a way that doesn’t allow separeting static files and PHP files, that seems to be a bad design in 2024. I know CMSs can include modules that can include static files liek css and js, but those files can often be cached into a single folder or sometimes even a single file when the filetypes are the same. In that case there os often a public
or similar folder which is the only one to mount to nginx or other webserver and everything else can go to the PHP container only.
When you have all the files in the image and you mount only uploaded files and other kind of data into the container, you can still use a volumes where the volume name includes the version number of the image. For example:
services:
nginx:
volumes:
- type: volume
source: data
target: /docrootpath
depends_on:
- php
php:
image: yourapp:${APP_VERSION}
volumes:
- type: volume
source: data
target: /yourapp-path/public
volumes:
data:
name: "projectname_public_${APP_VERSION}"
Then yo have to reattach the new volume by using --renew-anon-volumes
docker compose up -d --renew-anon-volumes
Yes, I know, it is not anonymous, but it worked for the named volume as well. Without this, the new volume would be created but not attached to the container.
Since the nginx depends on the PHP, when a new version is set in the .env
file for example, it will chaneg the image tag and also create a new volume before nginx starts, the public files are copied to the volume so nginx can mount it. The old volume will still be there. You can handle that manually after checking that everything is right so if you need, you can roll back to the old version, or you can write a script for the deployment which immediately deletes it, or you can run it in a cronjob to remove old versions of these files. Of course, if that public folder is really static, it is not even necessary to keep it since you can roll back and the old image will start to copy the old files to the old volume again.