PHP-FPM executing old code after symlink deploy

Does your site seems 'stuck' on the old version after a deploy? PHP-FPM might be holding onto a symlink.

If you are deploying your PHP projects using a tool such as Deployer, Capistrano or Rocketeer, you might have noticed that your site seems 'stuck' on the old version after a deploy.

To understand why this happens, we need to take a look at the deployment strategy used by these tools. They work by managing a particular directory structure:

  • A releases directory with versioned subdirectories. This is where your source code lives.
  • A current directory, which is a symlink to the currently active version. This is where you point the docroot of the webserver to.
  • A shared directory which stores files unrelated to your source code (eg. uploaded images).

The directory structure might look like this:

| var
|-- www
  |-- current -> /var/www/releases/2
  |-- releases
  |  |-- 1
  |  |-- 2
  |-- shared

If you now want to deploy a new version of your site, you would upload it to a new directory: releases/3.

| var
|-- www
  |-- current -> /var/www/releases/2
  |-- releases
  |  |-- 1
  |  |-- 2
  |  |-- 3
  |-- shared

After all code has been uploaded, the current symlink can be updated to point to the new version. The advantage of this strategy is that this switch happens instantly. You don't end up in a situation where only half your files have been updated to the new version while the deploy is running.

| var
|-- www
  |-- current -> /var/www/releases/3
  |-- releases
  |  |-- 1
  |  |-- 2
  |  |-- 3
  |-- shared

At this point, you might notice your users are still being served from releases/2. Or, more specifically, static files are being served from releases/3, but PHP scripts are still being executed from releases/2.

This happens because PHP-FPM caches the resolved symlink for a period of time. Luckily, this situation can be easily remedied by reloading the PHP-FPM service. The exact command depends on your operating system, but for those using systemd it would be: systemctl reload php-fpm. Even better is that all the deployment tools mentioned earlier have a functionality to run commands after the deploy, so this process can be completely automated. For example, in the case of Deployer you could add after('deploy:symlink', 'php-fpm:restart');.

Happy deploying!