php[architect] logo

Want to check out an issue? Sign up to receive a special offer.

Deploying Our Code With Deployer 7 0

Posted by on September 3, 2022

As a developer, you probably love to create new features in your applications to help your users solve their problems. Eventually, you have to get that code to a server so it can be used.

If you’re like me and are using a framework, that process can involve multiple steps. You might need to SSH into a server, pull down the latest changes from Git, run all your migrations, and then perform framework-specific tasks like resetting four or five different caches individually (I’m looking at you Laravel).

I once worked for a company where the process to deploy new code took over 20 steps. We had to create a deployment checklist because if we missed one of those steps, it could take the whole site down. Heaven help you if you ran into a situation where you needed to roll back all of those changes.

Thankfully, you can use the Deployer package to remove this complexity from your life. Deployer 7.0 was just released, so I thought now would be a wonderful time to go over it.

What is Deployer?

Deployer is a PHP package that provides automatic server provisioning, zero downtime deployments, rolling back to a previous release, and ready-to-use recipes for the major frameworks and some PHP applications.

Now let’s talk about what a zero downtime deployment is.

As we’re deploying our code, it’s possible to have some files as part of one “deployment” and some of it as a different “deployment” inside of the same directory. If a user starts out with a file in deployment A and then tries to access a file in deployment B, they can get runtime errors.

Deployer prevents this by creating a new directory for each release and then it switches to it all at once. It also helps with caching because the new path invalidates the file caches.

Deployer also has support for rolling back to a previous release based on the way that it creates these zero downtime deployments.

Deployer uses recipes to knows how to install your application. It comes out of the box with support for multiple frameworks, but we can also add our own and add on custom steps.

Installing Deployer

Installing Deployer is done through composer.

composer require deployer/deployer

To aid in using the package, it installs a script to the “vendor/bin” directory called `dep`.

The instructions on their website actually recommend setting up an alias. So all you have to type in is `dep` instead of `./vendor/bin/dep`.

alias dep=./vendor/bin/dep

Depending on how often you deploy your code you may not want to do this. I highly recommend it and I will be doing so for this article just to make the examples a little bit shorter.

If you’re ever lost, you can run `dep list` to get a list of all the available commands.

dep list
Deployer 7.0.0

command [options] [arguments]

-h, –help Display help for the given command. When no command is given display help for the list command
-q, –quiet Do not output any message
-V, –version Display this application version
–ansi|–no-ansi Force (or disable –no-ansi) ANSI output
-n, –no-interaction Do not ask any interactive question
-f, –file=FILE Recipe file path
-v|vv|vvv, –verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
blackjack Play blackjack
completion Dump the shell completion script

Initializing and First Deployment

Before I started this next phase I did the following.

1. I had a test Laravel project
2. I created a virtual machine using [Cloudways](
3. I noted the path for the application in Cloudways
4. I create a user account for the application in Cloudways and copied over my SSH keys so I don’t have to type in my password over and over.

Next, I’m going to run `dep init` and to walk us through the steps to set up our deployer project.

It’s going to ask us a bunch of questions, but don’t panic if you don’t know what to put, we can always go back and change it later.

Now the first thing it’s going to ask us is whether or not we want to use PHP or YAML as our way of storing our information. I like using PHP because if I need to, I can write PHP code in that file.

$ dep init
│ │
│ │
│ ____ _ │
│ | \ ___ ___| |___ _ _ ___ ___ │
│ | | | -_| . | | . | | | -_| _| │
│ |____/|___| _|_|___|_ |___|_| │
│ |_| |___| │
│ │
│ │

Select recipe language [php]:
[0] php
[1] yaml

The next thing it’s going to do is ask us which type of project we’re doing now. This is a Laravel project, but we could just as easily pick a CakePHP project or a Symphony project or any of these other things that exist within here.

Select project template [common]:
[0 ] cakephp
[1 ] codeigniter
[2 ] common
[3 ] composer
[4 ] contao
[5 ] drupal7
[6 ] drupal8
[7 ] flow_framework
[8 ] fuelphp
[9 ] joomla
[10] laravel
[11] magento
[12] magento2
[13] pimcore
[14] prestashop
[15] provision
[16] shopware
[17] silverstripe
[18] statamic
[19] sulu
[20] symfony
[21] typo3
[22] wordpress
[23] yii
[24] zend_framework

It’s going to ask me what the repository is for my project.

Repository []:

It’s asking me for the project name, and in this case, it’s actually `public_html`, which is what I got out of Cloudways.

Project name [www]:

And then it’s asking me for my hosts.

Hosts (comma separated) []:

The final output of the script tells us that it created a deploy.php file.

Successfully created deploy.php

This is where deployer stores it’s configuration.

namespace Deployer;

require ‘recipe/laravel.php’;

// Config

set(‘repository’, ‘’);

add(‘shared_files’, []);
add(‘shared_dirs’, []);
add(‘writable_dirs’, []);

// Hosts

->set(‘remote_user’, ‘deployer’)
->set(‘deploy_path’, ‘~/public_html’);

// Hooks

after(‘deploy:failed’, ‘deploy:unlock’);

Now at this point, if we were using just a “raw” virtual machine, we would run the `dep provision` command, which would go out and install all of the different packages that we need in order to get our project up and running. We’re not going to be doing that because Cloudways already does that for us.

Our next step is to do our first deployment. And to do that we’re going to do `dep deploy`.

$ dep deploy
task deploy:info
[] info deploying HEAD
task deploy:setup
task deploy:lock
task deploy:release
task deploy:update_code
task deploy:shared
task deploy:writable
task deploy:vendors
task artisan:storage:link
task artisan:config:cache
task artisan:route:cache
task artisan:view:cache
task artisan:event:cache
task artisan:migrate
task deploy:symlink
task deploy:unlock
task deploy:cleanup
task deploy:success
[] info successfully deployed!

Now we’ve successfully deployed our application.

You may notice that there’s a warning that our `.env` file is empty. This will only happen the first time we use deployer on this directory. After that it will reuse the `.env` file we create.

Rolling back our changes

There’s going to come a time where we accidentially deploy code we don’t want to have our customers using. We might have a typo in the code or a misspelling.

We could log into the server and try to find the file and fix it, or we can just roll back that release.

By using `dep releases` we can see the last set of releases on our server.

$ dep releases
task releases
+---------------------+-------------+---------- ---+--------+
| Date (UTC) | Release | Author | Target | Commit |
| 2022-09-01 12:41:53 | 1 | Scott Keck-Warren | HEAD | hash |
| 2022-09-01 12:43:40 | 2 (current) | Scott Keck-Warren | HEAD | hash |


Now all we have to do to rollback from the current release, which is two, to the previous release, which is one, is just run `dep rollback`.

$ dep rollback
task rollback
[] Current release is 2.
[] Rolling back to 1 release.
[] rollback to release 1 was successful

Now, if we run `dep releases` again, we can see the release two has been marked as bad and the release one is back as the current.

$ dep releases
task releases
+---------------------+-------------+---------- ---+--------+
| Date (UTC) | Release | Author | Target | Commit |
| 2022-09-01 12:41:53 | 1 (current) | Scott Keck-Warren | HEAD | hash |
| 2022-09-01 12:43:40 | 2 (bad) | Scott Keck-Warren | HEAD | hash |



Deployer uses recipes to determine the structure for how we deploy our application. We can use `dep tree deploy` to see the specific step.

$ dep tree deploy
The task-tree for deploy:
└── deploy
├── deploy:prepare
│ ├── deploy:info
│ ├── deploy:setup
│ ├── deploy:lock
│ ├── deploy:release
│ ├── deploy:update_code
│ ├── deploy:shared
│ └── deploy:writable
├── deploy:vendors
├── artisan:storage:link
├── artisan:config:cache
├── artisan:route:cache
├── artisan:view:cache
├── artisan:event:cache
├── artisan:migrate
└── deploy:publish
├── deploy:symlink
├── deploy:unlock
├── deploy:cleanup
└── deploy:success

Again, because this is a Laravel project, there are a bunch of artisan commands within our tree.

Advance usage.

This is just a fraction of the features that deployer provides.

My favorite part is that we can add custom tasks to our recipe. For example, we might want to send a slack notification to our team informing them that the code has been deployed.

task("deploy:notification", function() {
Slack::Notify("Deployment Successful!");

after(“deploy”, “deploy:notification”);

What You Need to Know

  • Deployer helps us deploy our code
  • Uses recipes to create a deployment checklist
  • Allows us to revert changes
  • Can add custom tasks


Your Host

Scott Keck-Warren

Scott Keck-Warren

Tags: ,

Leave a comment

Use the form below to leave a comment: