Automating and Standardizing Drupal Deployments with Ansible

What does it take to get a Drupal codebase onto a server and make it ready to serve a website? There are more steps than expected, and it is easy to forget the details if you don’t do it frequently. Additionally, many of these same steps need to be performed during each code deployment. Just a few of the steps include:

  • Get the latest code.
  • Run Composer commands.
  • Create a public files directory.
  • Run deployment commands.
  • Build theme assets.
  • Secure file permissions.

Automating the Setup

At Chromatic we love to automate processes like this. Even if the effort requires only one or two manual steps, repeated many times per day they can add up. The value of automating these tasks is well worth the effort. Our automation journey began with a humble shell script, but we soon realized this was difficult to maintain and that our one size fits all solution didn’t fit the needs of all our clients.

Selecting Ansible as an Automation Tool

Ansible is a flexible tool that we use for tasks as big as provisioning and managing servers, to as small as running cron on all our Drupal sites with one command. Some of the key factors that helped us decide to use it for automating deployments were:

  • Ansible is idempotent and allows us to define the desired state, only taking action when necessary to achieve that state as opposed to a script that would just run a specific set of commands.
  • Ansible does not depend on any agents. If you can SSH to a box, Ansible can act on that box.
  • Ansible configuration is stored in YAML, and we like YAML.
  • Ansible has a lot of open-source role support via Ansible Galaxy.

Required Features

Git Operations

The deployment tool needs to support git operations to clone the latest version of your codebase with configurable branches.

Initial Site Deployment

During early development, a site might not have a persistent database. The deployment tool needs to support installing from site configuration using the drush si command.

Existing Site Deployment

Deploying to an existing site needs to be supported. A variable sites/* folder name should allow for per-site targeting. Standard Drupal deployment commands such as drush deploy can be run as well.

Configuration Checking

Keeping the tracked configuration up to date and in sync with the active config in the database can be hard at times. An optional deployment step is needed that checks if they are in sync and can fail the deployment if they are not.

File Permission Management

The deployment process needs to handle setting the proper permissions for the various files and folders within Drupal such as the settings.php file and the public files directory.

Ansible Setup

Setup requires the addition of several files to your repository and the installation of Ansible on whatever machine you will be triggering the deployment from. This is probably going to be your local environment for development and some other environment for production (ie. a server of your own, GitHub Actions, Jenkins, etc). Below are examples of the files with some basic defaults. These can be expanded to accommodate more complex needs. Note that we store these files in an ansible/ directory so the paths reflect that.

ansible/requirements.yml tells Ansible which roles it needs to install when it runs. In this case, we only need our chromatichq.deploy-drupal role.

# Ansible required roles.
- src: chromatichq.deploy-drupal
version: 2.20

ansible/hosts.yml defines for Ansible the hosts it is able to connect to.

ansible_connection: local

ansible/example-playbook-configuration.yml provides the configuration that allows for customized deployment options to fit your codebase and hosting environment.

- hosts: all

deploydrupal_repo: ""

deploydrupal_checkout_user: "root"
deploydrupal_apache_user: "www-data"

deploydrupal_dir: "/var/www"
deploydrupal_code: false
deploydrupal_redis_flush: true
deploydrupal_redis_host: "redis"

deploydrupal_npm_theme_build: true
deploydrupal_npm_theme_path: "{{ deploydrupal_dir }}/web/themes/chromatic"
- yarn
- yarn build:preview

deploydrupal_config_check_structure: true

- ansible-deploy-drupal

The call to run the playbook will look something like this:

ansible-playbook -v --limit=EXAMPLE_SITE_PRODUCTION -i "/path/to/repo/ansible/hosts.yml" "/path/to/repo/ansible/example-playbook-configuration.yml"

Our Solution

With the considerations above, we created the chromatichq.deploy-drupal Ansible role. It standardizes the entire process with best practices provided through sensible defaults and many optional configuration options to fit custom needs. All of the steps are defined in YAML using standard Ansible tools.

Testing Deployments

We are big proponents of using Tugboat, to create a testing environment for every pull request. Using the chromatichq.deploy-drupal role allows us to test our standardized deployment process along with any changes made to the site’s codebase. This gives us increased confidence that what we see on Tugboat/QA is what we can expect when deploying to production.

Benefits of Automation & Standardization

Deployment best practices evolve and bugs will be found throughout the life of a website. When that happens, the use of a centralized Ansible role allows us to easily contribute them back to a repository where all of our projects can benefit from them without maintaining a deployment script in every site repository. We also get a chance to clearly document the what and why of the changes as the update is no longer just a fix in an application repo, but it is often a new release of a tool with proper documentation.

No tool will fix every problem, and our Ansible role for standardizing Drupal deployments is no exception. However, we love having a process that allows us to easily set up standardized deployments with minimal effort to keep our sites deployed correctly and securely. We look forward to hearing how you use the tool, and collaborating on refining and improving the deployment process together.