Remember those days when you could easily deploy something to production without executing each time a number of commands and clearing cache a few times meanwhile? Yeah, me neither.
Every modern framework or platform has their own individual perks that shape our development and deploying process. Magento is no different here and its deployment process can be a challenge for newcomers.
In this post, we will introduce Deployer, automated deployment tool for PHP.
Since Deployer is just a PHP application distributed as a PHP Archive (.phar), extending its functionalities and customizing deployment tasks are quite easy. Most importantly, if something goes wrong, we see few PHP exceptions now and then so they can be fixed in no time.
We will start by downloading it down and setting it globally executable for our user
curl -LO https://deployer.org/deployer.phar mv deployer.phar /usr/local/bin/dep chmod +x /usr/local/bin/dep
Now you can execute dep command which will run Deployer. Position yourself at the root of your project and run
which will trigger the Deployer config generator. Here it will ask us about our project type, but that only means it will require a specific recipe. Recipes are nothing more than already written tasks for deploying a specific framework or platform.
As you can see, Magento is not listed here, but since we know it is part of the code by looking at source, we will start by manually creating deploy.php file to avoid refactoring. In this file we will require Magento 2 recipe like this:
namespace Deployer; require 'recipe/magento2.php';
Magento 2 recipe already contains almost everything we need for deployment, we just need to make a few adjustments, like adding the project name and project repository:
// Project name set('application', My awesome project); // Project repository set('repository', 'firstname.lastname@example.org:username/my-awesome-project.git');
We still didn’t define where we want to deploy, so we will start by defining a host.
For this simple example we will define only our local machine, but in practice, there is probably more than one environment where we want to deploy, like staging or production, so it is best to keep those definitions in a separate file.
In the root of your project create a new file called hosts.yml and write something like this inside:
localhost: stage: dev local: true branch: master deploy_path: /var/www/my-awesome-project
For different stages like staging or production, we most likely need to do an ssh which we don’t need in our example so you won’t need this local: true line, but you will have to add one more line at the end:
Some of your teammates can connect in a different way so it is best to keep that information at config level. Now when we have this defined, we can tell in our deploy.php where is this hosts file located:
// Hosts inventory('hosts.yml');
And that is, actually, all there is. You can now run:
dep deploy dev
Well actually, there is an exception in the process. I got
[Exception] Warning: array_replace_recursive(): Argument #1 is not an array in /var/www/my-awesome-project/releases/1/vendor/magento/framework/App/DeploymentConfig/Writer.php on line 135
That is because of missing env.php since cloned repository doesn’t have this file, so we will create it ourselves, or it could be already created for you by Deployer but it is empty.
Change the directory to your deploy path defined in hosts.yml and from here on you can edit this file on path shared/app/etc/env.php.
We will explain in a moment why is it located there, but don’t forget to set a production mode in here or you will get another exception if you try to deploy again with our current settings. Of course, this is done only once.
Let’s try again running
dep deploy dev
And yes, this time everything went fine:
How does it work
There are a lot of tasks that are executed but in theory the simplest explanation is that when you run command for deploying, Deployer will make an SSH to a specified remote server (unless we explicitly say that we want to deploy to local environment), create a new release folder and in it clone the VCS repository.
With the magic of symlinks, it will point your latest release to the current directory.
After deployment, you will see three main directories:
Releases are your release folder where the code is cloned from the repository, while current is symlinked to the latest release.
In the current directory, there are symlinks to also files in a shared directory that contains something that is the same/shared for all releases, like env.php or media folder.
In practice, if you want to use Deployer for deployment of Magento 2 projects, your server should point to current/pub directory.
In this post, we haven’t created our custom tasks or overridden some of the existing ones, but I hope you get the basic idea how Deployer works and what is the idea behind it.
And just one final pro tip
If you are running PHP-FPM on the server where you are deploying, create a new task for restarting this service after the deployment is done, because the symlinks can be cached and your server will end up serving the older release. Trust me, I learned this the hard way.