Upcoming book:

Modern .NET Development with Azure and DevOps

PHP Symfony in Azure App Services

Introduction

While I am not a PHP developer, I recently had to figure out how to deploy it to Azure App Services. I found the documentation to be both lacking and confusing, so I thought I would write up a quick guide to help others.

The Azure docs mix Apache and nGinx configurations, and Symfony stopped documenting how to deploy to App Services altogether.

By the way, Azure App Services uses nGinx, not Apache, for PHP 8.x runtime.
Ignore any Microsoft Learn documentation that references Apache for PHP apps because it hasn't been updated.

Why Azure App Services?

Azure App Services is a great way to deploy your app to the cloud. It's easy to set up, easy to configure, and easy to deploy to.

One of the main reasons I like Azure App Services is that it's easy to scale and maintain, compared to traditional VM hosting.

This post aims to help you modernize your PHP app by using Azure App Services.

Prerequisites

I assume you have a basic understanding of Azure App Services and Azure DevOps.
I also assume you have a PHP app that you want to deploy to Azure App Services.
Finally, I assume you have a basic understanding of PHP and Composer.

I will be using PHP 8.2 and Symfony 6.2 in this guide, but the steps should be similar for other versions.

Getting Azure App Services ready

To keep things simple, we'll be using the Azure Portal to create our App Service.

Start by logging in to the Azure Portal and going to the Create Web App screen.

Importantly, we'll be using the "Code" option to create our App Service. Make sure you select the "PHP 8.2" option in the "Runtime stack" dropdown. The rest of the settings in the wizard are up to you, as they don't affect the outcome for this post.

Once the App Service is created, be sure to head over to the Configuration tab (under Settings) and create any Application Setting that your application needs. For example:

  • APP_ENV - prod
  • APP_DEBUG - 0

The last thing we need for now is the deployment credentials. Head over to the Deployment Center tab (under Settings) and click on the "FTPS credentials" link. Copy the FTPS endpoint, username, and password to a safe place. We'll need them later.

Configuring nGinx

Before we can deploy our app, we need to configure nGinx to serve our app. This is because the default nGinx configuration is not suitable for Symfony applications.
Since the nGinx configuration is stored outside the app's directory, we'll need to create a new configuration file and replace the original one.

The nGinx configuration file used for the web app is located at /etc/nginx/sites-available/default. To start, connect either through FTPS or SSH to your App Service and grab the file's contents.

Changes that you'll want to make to the file are:

  • Change the root directive to point to your app's public directory. The root directive should be root /home/site/wwwroot/public;
  • Change the / location to execute the your index.php file. You likely want a configuration similar to the following:
location / { 
  index index.php;
  try_files $uri /index.php$is_args$args; 
}

If you need to refer to the app's domain name from code (and don't want to hard-code it), you need to tell nGinx that you are using HTTPS.
For this, under the location ~ [^/]\.php(/|$) directive, add:

fastcgi_param   REQUEST_SCHEME  'https';
fastcgi_param   HTTPS           'on';

Once you've made the changes, save the file to your source code repository. For example, I saved it to azure/nginx.conf.

Optional - configure PHP settings

If you need to change any PHP settings, you can do so by creating a php.ini file and adding it to your source code repository.
Azure App Services uses the php.ini file located at /usr/local/etc/php/php.ini.

Remember that anything that you want to store needs to be under the /home path, so, for example, I configured logs like following in my php.ini file:

error_log = "/home/LogFiles/php-error.log"

Creating a startup script

The last thing we need to do is create a startup script that will run when the App Service starts our app. This script will replace the nGinx configuration, replace the php.ini file, restart nGinx, and restart PHP.

Remember to update the paths in the script to match your app's directory structure. Keep in mind though that your app's directory structure should be under /home/site/wwwroot/.

cp /home/site/wwwroot/azure/nginx /etc/nginx/sites-available/default
cp /home/site/wwwroot/azure/php.ini /usr/local/etc/php/php.ini

pkill -o -USR2 php-fpm

service nginx reload

cd /home/site/wwwroot

php bin/console cache:clear

Save that file to your source code repository. For example, I saved it to azure/startup.sh.

As the Symfony docs mention, you may want to run other tasks as part of the startup script.
Do remember that your application will not be available until the startup script has finished running, so you want to keep it as fast as possible.

Deploying to Azure App Services

Now that we have our App Service ready and our source code ready, we can deploy our app to Azure App Services.

Make sure you optimize your vendors as usual through composer install --no-dev --optimize-autoloader before starting the deployment.

Before starting the first deployment, head over to Configuration tab (under Settings) and, under the "General settings" section, set the "Startup command" to bash /home/site/wwwroot/azure/startup.sh (or the path were you stored your script).

Now, using your favorite FTPS client, upload your app's source code under /home/site/wwwroot and be sure you include the 3 files we created.

After restarting the App Service, your app should be up and running!

Extra tips

Security

Remember that Azure App Services allows you to configure environment variables that are securely sent over to the container running your application.
This is an excellent way to store sensitive information such as database credentials, API keys, etc.

Reliability

If you want to improve the reliability of your app and prevent downtime while your application starts, you can set up Deployment Slots and use them to test your changes before deploying them to production.

You can also set up the App Services Health Check to ensure that your app is running correctly and receive alerts when it's not.

Monitoring

While PHP applications are not supported for the Application Insights integration, you can set up Diagnostic settings in your application to send logs to an Azure Log Analytics Workspace so that you can monitor your application's health and facilitate searching logs.

Conclusion

I hope you find that spending the time to set up Azure App Services to be worth the effort.

I hope this post helps you modernize your PHP app by using Azure App Services. If you have any questions or comments, please feel free to reach out.

Happy coding!