Adding PHP Service0:00
At the bottom of our docker-compose file, we'll add in a new service heading called php. Just like we did with nginx, we're going to build this off of a custom docker file instead of an image from the Docker Hub. It'll be called php.dockerfile. This way we can make a few adjustments to the php service that will come in handy when it's being used for our application's environment. We'll also attach a volume to. In fact, the same one as the nginx service, source, to /var/www/html. Okay, let's create that php.dockerfile and open it up.
Creating PHP Dockerfile0:32
In fact, the same one as the nginx service, source, to /var/www/html. Okay, let's create that Dockerfile and open it up. First we need to determine a base image to build it off of. Let's use FROM php:8-fpm-alpine. Remember we're using the alpine build for the nginx container as well since it's a lightweight distro and helps cut down on space that our containers take up. Also like the nginx image, we're going to create a custom user that will be used to run php inside of this container. Let's create a couple of env variables for phpGroup and phpUser, both right now set to Laravel.
Let's create a couple of env variables for phpGroup and phpUser, both right now set to Laravel. Then run adduser g, passing in the phpGroup variable, sbin/sh d, and passing in the phpUser variable. Moving on, let's make some adjustments in our main phpConfiguration file to change the user and group that php runs as. Like with the nginx container, we could do this by copying in a new config file with add, but the phpConfiguration file is pretty big and we are only changing a really small portion of it. Instead we can use the sed command again to do a find and replace for the values that
Installing PHP Extensions1:53
portion of it. Instead we can use the sed command again to do a find and replace for the values that we want to change. Run sed -i, which will replace that user from the one in our .env variable. And the last argument being the file path is /user/local/ptc/phpfpm.d/www.conf, and we'll copy over the exact same command, replacing user with group. Next run mkdir -p /var/www/html/public to get our application folder ready. And speaking of application, Laravel needs a few extensions to work properly. Some are included in this docker image that we're building this off of, but some aren't. Especially the pdo extension for the mysql database.
Some are included in this docker image that we're building this off of, but some aren't. Especially the pdo extension for the mysql database. Normally when setting up a server, we'd have to run a couple of commands to get the extension file and then uncomment lines from our php.ini file. With docker though, it's a lot easier. There's a handy built-in command, docker phpext install, and then we just pass in the extension names, pdo and pdomysql. This command will download any extension files that we need, make any adjustments to the config files, and load them in. And finally, we need to modify the default command that this container runs when spinning.
the config files, and load them in. And finally, we need to modify the default command that this container runs when spinning up. CMD lets us change that. Like we learned earlier, this takes an array of strings, each one representing a part of a command normally separated by a space. This is called the exec form of writing commands in Dockerfiles, and it makes it harder to have string or character mistakes with the commands passed in. We need to run php-fpm, the -y flag, and our config file, /usr/local/etc/php-fpm.d/www.conf, and finally the -r flag, which allows php to run as root, the whole reason that we're doing
Explaining User Permissions3:44
We need to run php-fpm, the -y flag, and our config file, /usr/local/etc/php-fpm.d/www.conf, and finally the -r flag, which allows php to run as root, the whole reason that we're doing this in the first place. Before we continue, I just want to say a quick aside about why we're adding lines for custom users and letting php run as root. Depending on your host operating system, the user, group, and permissions from the files that you're mounting as a volume can transfer over into the container. So here on my machine, my user is Andrew, and my group is staff. But if matched in the container, the php process wouldn't be able to write to certain directories or files, causing errors to crop up in our application.
But if matched in the container, the php process wouldn't be able to write to certain directories or files, causing errors to crop up in our application. So instead, one of the ways that I've learned to combat this is by mirroring the user and group on our host machine into the container, and I'm doing that with these env variables in our Dockerfile. I'm on macOS though, which has a virtualization layer between the file system on my computer and Docker, so it really doesn't matter in my case. But if you're using Linux or Windows or WSL2, you may experience this and have to change these variables to suit the user and group that your files are under. Okay, we have a php container put together, let's test it out.
Testing PHP with Nginx5:02
these variables to suit the User and Group that your files are under. Okay, we have a php container put together, let's test it out. In our source directory, let's create a file called index.php and open it up. Inside of it, we'll just echo out the phpinfo information. Save this, and let's remove that index.html file that was there earlier. And we just need to make a quick adjustment to the nginx.conf file. We'll need to add a block in here to process our php files through the php-fpm process. This is all pretty boilerplate, with a small exception of the fastcgi_pass line. Normally this would be prefixed with unix or the localhost IP address, but instead we're using php here.
Normally this would be prefixed with unix or the localhost IP address, but instead we're using php here. The reason for that is because we want to utilize the 9000 port, but not on our local container which in this case is running nginx. Instead we want it on the php container that we just got done setting up. With docker compose, you can directly access another container in the same network by using that container's service name in the same place you might use a domain or an IP address. So the php container, and the 9000 port. Let's go back to our terminal, run docker compose up --build, which rebuilds our containers, and spin them up.
Fixing Startup Dependencies6:30
Let's go back to our terminal, run docker compose up with the build flag, which rebuilds our containers, and spin them up. Uh oh, our nginx container exited, and we got this error host not found in upstream php. The reason for that is that the nginx container started before the php container did. So in order to fix this, let's open up our docker-compose.yml file again. And under the depends_on, beneath mysql we'll also add the container for php. I actually also specified the wrong location of this conf file. It should just be /usr/local/etc/php-fpm.conf. Heading back to our terminal, let's run docker compose up --build.
It should just be user local etc phpfpm.conf. Heading back to our terminal, let's run docker compose up --build. Okay, everything looks like it's good. So let's take a look in the browser at localhost. Oh, well we get a 404 not found. I think that's because we added our index.php file in source instead of source/public. Let's fix that real quick. Because we're using volumes, we can do that live and see it update without having to bring down and back up our containers. And there we go, we can see that phpinfo() called being returned back, and it details
down and back up our containers. And there we go, we can see that php info called being returned back, and it details all the info for the current runtime that we built for the php container. In the next video, we'll use Composer to install a clean Laravel app and maybe even a helpful package too.
