Using Deployer To Deploy Drupal 8 Sites

Deployer is a PHP based deployment tool that I have been using very successfully for a number of months now. It comes with a number of different recipes to deploy a variety of PHP based applications, including Laravel, Symfony, Yii, and Drupal.

I first found Deployer when looking for a deployment package that suited my needs. Phing had been my tool of choice for a number of years, but it had proved problematic trying to debug the XML syntax when things needed tweaking. As I was deploying PHP applications it made sense to me to use a PHP based system to do the deployment. I looked at tools like Capistrano, but as I wasn't that familiar with Ruby I was worried that I wouldn't be able to debug mistakes or customise the deployment process easily.

I have been using Deployer ever since to deploy this site, and after my 100th deployment things are still running smoothly. I've been so impressed with the tool that I thought I would put together a quick guide on getting up and running with it.

Getting Set Up

My usual way of using tools like this is to include it in the project itself as a dev dependency, rather than install them globally. The first step is to install deployer as a dev dependency in your composer file.

composer require --dev deployer/deployer

After this you can run an initialisation command to get things setup with Deployer.

./vendor/bin/dep init

This will ask a few questions about what platform you are using and where the git repository for the project is being hosted. When finished you will have created a file called deploy.php in the root of your project.

The following is an example of a deploy.php file for Drupal 8 created during this process.

<?php
namespace Deployer;

require 'recipe/drupal8.php';

// Project name
set('application', 'my_project');

// Project repository
set('repository', '[email protected]:gitaccount/gitrepo.git');

// [Optional] Allocate tty for git clone. Default value is false.
set('git_tty', true); 

// Shared files/dirs between deploys 
add('shared_files', []);
add('shared_dirs', []);

// Writable dirs by web server 
add('writable_dirs', []);

// Hosts
host('project.com')
    ->set('deploy_path', '~/{{application}}');    
    
// Tasks
task('build', function () {
    run('cd {{release_path}} && build');
});

// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');

Out of the box I needed to tweak a few settings to get things working.

The directories that are used by Drupal to store user files needed to be updated in my setup. By default Deploy assumes that your Drupal codebase will be flat, i.e. the web root will be in the root of the repository. My codebase is a composer setup using a directory called 'docroot' as the web directory. This requires a change to the shared files directives in order to point them at the right place.

The special variable drupal_site can be used here, by default this points to 'default' and so can be used here to point to the correct directory if you are using a multi-site setup. The shared_dirs parameter here points to an area in the deployment directories that is shared, I'll come back to this point later. The writeable_dirs parameter allows Deployer to set the correct permissions on certain directories so that they can be written to by the website. Here is what I changed for my own deployment file.

set('shared_dirs', [
  'docroot/sites/{{drupal_site}}/files',
]);

set('writable_dirs', [
  'docroot/sites/{{drupal_site}}/files',
]);

You might notice that the default deploy.php file allows you to add to these directives, whereas in the above example I have overwritten them with the set() function. This is because I wanted to simply overwrite them and not add to the values.

Next you'll need to setup the parameters used to connect to the remote server and where the deployment directory is located. I have previously talked about how to set up a user that will allow you to connect and deploy correctly using Deployer. The following expands the default created hosted settings by also setting a user and port to connect through.

host('127.0.0.1')
    ->user('deployer')
    ->port(22)
    ->set('deploy_path', '/var/www/html/myproject');

Further customisations can be made to other existing parts of the deploy.php file. One part that you might want to tweak is the task() function. This function is held in the drupal8.php file (included at the top of the deploy.php file) and is used to inform Deployer what to run during the deployment. You can copy this from the drupal8.php file and modify it as you need.

task('deploy', [
    'deploy:info',
    'deploy:prepare',
    'deploy:lock',
    'deploy:release',
    'deploy:update_code',
    'deploy:shared',
    'deploy:writable',
    'deploy:symlink',
    'deploy:unlock',
    'cleanup'
]);

Deployment Structure

The tool itself works by creating a set of standard directories that it deploys into with the deploy_path directive being the root directory. Inside this directory you will see a structure like this.

.dep
current
releases
shared

The .dep directory contains a file called releases, which tracks the release history of the application. Every time you deploy a counter will be incremented and the current time recorded. The releases are stored in the releases directory, with each release corresponding to a number. The following is the current release directory for this site at the time of writing.

# ls releases/
100  96  97  98  99

By default, this is set to keep the 5 latest releases. You can set the number of releases to keep by using the keep_releases setting in your deploy.php file.

set('keep_releases', 5);

The current directory in the Deploy structure is actually a symlink to the current release. When you run a deployment the tool will create another directory in releases and deploy your application there. If the deploy tasks were successful it will recreate the 'current' symlink from the old release directory to the new. The good thing about this is that deployments are atomic, in that they happen as a single directory change instead of any lengthy downtime for the site. The final step will only happen if the deployment process did not encounter any issues, which saves you from launching a broken site. In addition, a rollback can be performed to put the code back to the previous version without any major disruption to the site.

The final directory here is the shared directory. This is where your Drupal user uploaded files are stored, you can also optionally add in other files like settings.php or services.yml by adding them to the shared_files directive. When you deploy, as well as creating a symlink to the release directory, Deploy will also create a symlink between your Drupal files directory and this location. You will see a similar folder structure being created in this directory (ie, docroot/sites/default/files), which will have a symlink to the live site directory in the same place.

I haven't mentioned this yet, but what we are doing here is deploying into the web directory. In which case you'll want to set up your web server to point to the directory in the current symlink that serves your site. In the example above this is located at /var/www/html/myproject/current/docroot.

Running Deploy

With the configuration file in place and the root deployment directory created you can run a deployment. This is done by running dep with the deploy command.

./vendor/bin/dep deploy

The first time this happens Deploy will setup all of the directories needed in the deploy_path (.dep, releases etc), deploy your Drupal site into the correct place and then symlink everything together.

If you want to rollback just run the same thing but using the rollback command. All this does is to swap the symlinks back to their previous state.

./vendor/bin/dep rollback

There are also a number of other actions available to you. You can use the cleanup command to clean up any old releases, which might be useful if your application is large and you want to free up some server space.

./vendor/bin/dep cleanup

If you absolutely need to log into the server then you can do so quickly using the ssh command. This will use the credentials provided to the tool to log in and change directory to the correct place.

./vendor/bin/dep ssh

This is also a good way of testing the user credentials before you start running deployment tests.

One thing that has tripped me up on a couple of occasions is that Deployer will only deploy from the git repository, NOT from your local codebase. This means that if you forget to push your code and then deploy the deployment will run but actually fail to deploy anything. It will, however, use your local deploy.php file to run the deployment.

Ultimately, the use of Deploy has vastly decreased the complexity and increased reliability of my deployments. I would highly recommend using this system for your deployments, even if you are using a service like Travis to inspect your codebase before your deployments. You can also use Deploy to perform custom actions on your remote host so if you have any SCSS compilation steps then these can also be built into your process. I haven't covered this, but there is plenty of information on getting started with Deploy in their documentation.

More in this series

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
5 + 15 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.