How to configure Push to Deploy with Envoyer

|12 min read|
Teklog
Teklog


Introduction

It's hard to not stumble across Envoyer, once you become the member of Laravel community. It's advertised as Zero Downtime PHP Deployments and is one of many commercial products from Taylor Otwell, creator of Laravel framework.

I will walk you through the configuration of Push to Deploy. This means that whenever you push or merge your code into specified branch, Envoyer should detect it and automatically trigger deployment.

Keep in mind that Envoyer allows one project to be associated with exactly one branch from chosen repository.

Assumptions

  • you have git repository hosted on Bitbucket, Github or Gitlab
  • you have some cash to spend every month
  • you have no time nor knowledge of how to perform Zero Downtime Deployments

Solution

Head over to Envoyer, where you may register as

  • someone who creates and manages projects (paid, however 5-days free trial available)
  • someone who wishes to collaborates with others (free)

Once you do it, you will be presented with Connectivity page, where you can connect to the most popular GIT repository hosting services such as GitHub, BitBucket or GitLab.

FirstLogin Dashboard Min

Connect to one of available Code Providers - for this tutorial I've chosen Bitbucket. As you see, Envoyer uses oAuth and requests following information from your Bitbucket user.

BitbucketIntegration oAuth Min

If you hesitate a bit before granting the final access, it's a good thing. Personally I would prefer to have more granular and opt-in control over which repository is accessible by Envoyer - unfortunately it's not possible. One of the solution could be creating a special Bitbucket deployment user who has access only to subset of your repositories and then connect him with Envoyer.

After successful oAuth dance, you should be presented with following screen

Bibucketintegration confirmation min

Before you proceed to click big red Add Project button, please stop for a second and try to answer this question: Is there an easy way to delete the connection between Envoyer and Bitbucket?

You may go to your Envoyer profile, click the tab Integration and see this screen

Envoyer afterintegration min

Whoops, there is no way to remove the connection, just refreshing the tokens. Hopefully, there is a place where you can clean up your oAuth integrations - in this case head over to Bitbucket website and open your user's profile page. Navigate to Settings, then to oAuth (under Access Management) and find Envoyer under integrated applications.

BitbucketAccountWithEnvoyer min

Feel free to click Revoke button if you are already tired and don't want to continue. Otherwise let's see what Envoyer actually offers.

Start off by clicking aforementioned Add Project button. Specify what type of project it is and then type in your unique user/repository combination. As you see, Envoyer is tailored for Laravel-kind projects and in this tutorial I will focus on Laravel 5.4 deployment.

AddingNewProject min

After you added your first project, you will be presented with following screen

ProjectDetail EmptyServer min

At this moment, default branch is master, there are no deployments yet, nor any server configured. Post-deployment health checks are not in place either.

Before I show you how to Add a Server, it's good to know a bit more about Project Settings page (Accessible by clicking the Settings button), where you can do the following

Under The Basics section

  • rename your project
  • choose different type of project
  • define health check url (domain or IP)

Under Source Control section, as shown below, you may

  • change provider, repository or branch
  • opt-in for Deploy When Code Is Pushed
  • opt-in for Install "Dev" Composer Dependencies

ChangeProjectToDevelop min

And lastly, under Delete Project section, you may obviously delete your project by typing in its name.

Please make sure that Deploy When Code is Pushed is checked before you proceed any further.

Finally, we are ready to Add a Server, so let's do it together. Head over to your Project main page, choose Servers tab and then click Add Server button.

AddServer min

As you see, it was pretty straightforward, although some options need more explanation.

  • Your IP Address must be reachable from outside and your SSH Server configured properly
  • Your project must run on one of the following PHP Versions: PHP 7.1, PHP 7.0, PHP 5.6 with php5.6-fpm, PHP 5.6 with php5-fpm
  • You may decide to not receive Code Deployments which may be useful for Database Servers, where you still want to perform some hooks but not run any code
  • Project Path that you provided will be used by Envoyer to create releases directory and current symlink, so don't forget to adjust your web server's document root directory to something like /var/www/test-repository/current/public

Upon success you will be given Envoyer's public SSH key which should be placed in your server's .ssh/authorized_keys file.

You can monitor whether the connection was established by tracking Connection Status column under your Server list.

BeforePublicSShKeysPlaced min

As soon as you upload your Envoyer's public SSH key, click on the Refresh icon. If everything went fine, it should change to green.

Before we look into Deployment Hooks, it's good to know that you can configure more options during Server edition. In this case click Update Server button.

UpdateServer min

As you see, you can opt-in to Restart FPM After Deployments as well as specify your php and composer paths.

Ready for the final step of configuring Push to Deploy? Perfect, let's do it then together.

Head over to Deployment Hooks tab and look around. First thing to notice is four distinct actions which are fixed and cannot be deleted. You can hook into them by creating Before or After steps.

DeploymentHooks min

As you see I created four steps. Two after-steps take care of database migration and directory permissions - they will run after composer finishes installing its dependencies. Another two before-steps take care of NPM dependencies and assets compilation - they will run just before the activation of new release.

It's pretty simple to define new hooks, just click the Cog icon.

HooksList min

Being there, press Add Hook button and you will be presented with popover as shown below.

HookNew min

Envoyer is pretty flexible - you can use any shell commands that are available on you server.

Make sure you define Run As to a user which has acknowledged Envoyer's public SSH key. If you need to use many users, make sure you put the same Envoyer's public SSH key into this user's .ssh/authorized_keys file. (e.g: /home/newuser/.ssh/authorized_keys)

Keep also in mind that you need to specify on which server those hooks will run. If you add new server later on, you must come back to each hook you defined and manually check it there.

It's worth knowing that Envoyer provides three variables which should make your life easier.

{{release}} - the most current release directory (e.g: /var/www/test-repository/releases/20170704151411)
{{project}} - project's root directory (e.g: /var/www/test-repository)
{{sha}} - commit hash being deployed (e.g: 5d6fe4f7716f0f4be307ae59c2c9a2b62fa61d13)

We are almost ready to perform our first Deployment, isn't it exciting? As you know, Deployments may be triggered in three ways

What you may not know is that after you checked the Deploy When Code Is Pushed in your Project Settings, Envoyer silently created a POST Service in your Bitbucket's repository.

Head over to your Bitbucket's repository settings, choose newly created section Services (under Workflow) and on the right pane you should see pretty familiar URL.

BitbucketServiceForEnvoyer min

There is one gotcha though - remember this third option of deployments where you can post to url? On the Deployment Hooks tab, there is a little refresh icon next to this URL - once you click it, your automatic push to deploy will simply stop working! In order to fix it, head over to your Project Settings, uncheck and check the Deploy When Code Is Pushed option and then save the project - your Bitbucket's Service will be regenerated and your Push to Deploy should work again.

So you've set up everything, you also know now some of the common pitfalls, but still don't see any deployments. Why is that?

As Push to Deploy suggests, you must firstly commit something to your branch, then push it to your origin (e.g: https://tekmi@bitbucket.org/tekmi/test-repository.git) and patiently wait until Envoyer gets notified about this event.

You look at the Deployment tab of your project and all of the sudden the spinner shows off, announcing deployment - Envoyer's worker is ready to work on your server. With all that joy, please don't forget to click the Arrow button on the right, which will give you detailed information about deployment.

DeploymentOverview2 min

As you see, all the fixed steps as well as your hooks are launching in order you set before. You may want to click Documents icon to see even more details of a certain step - usually this will be print messages returned from your server's shell scripts.

If any of the scripts fails (returning non-0 shell status code), all the subsequent steps will be cancelled, stopping the whole deployment and marking it as failed.

Don't worry, your website should still be running and be in a perfect shape, since this was the Zero Downtime deployment. However here may be one gotcha.

Please consider situation when you had the database migration step and another script after it failed. In this case the deployment is cancelled, your website in happily in old state, but unfortunately the newest database changes have been applied - if you deleted some tables, you may be in trouble.

To remedy this, you have two options - either log in to your server and cast the command php artisan migrate:rollback or quickly fix failed scripts and reschedule previous deployment.

Situations like above are rare, so in most cases you should sleep well, without worrying too much about it.

Having gone through all the steps, it's time to enjoy freshly deployed project, heading towards browser and checking if everything looks like you expected.

Findings

  1. Laravel by default comes with some predefined JavaScript libraries listed out in package.json. They all are defined under devDependencies, so don't forget to run npm install --dev, even on your production server. Alternatively you can move all those libs under dependencies and remove the flag --dev.
  2. Installation of NPM dependencies take the most of the deployment time (in my case around 30-40s) and runs every time deployment is scheduled. One way to avoid it could be to use Yarn package manager. Another way could be to install NPM dependencies one level above release folder (using {{project}} variable), sharing it among deployments. Or maybe waiting until NPM dependency manager becomes configurable via Envoyer, like PHP Composer package manager is.
  3. Laravel's 5.4 bootstrap cache directory is not writable by default, causing Laravel project to fail. One way to tackle this would be to add sudo chmod -R 777 bootstrap/cache/.

Bugs

  1. If you use Push to Deploy deployments, there is no way to detect which event happened in your Bitbucket repository. As a consequence, if you merge your Pull Requests, Envoyer schedules as many deployments as you had commits under this Pull Request.
  2. If you add collaborator to your Envoyer project, this collaborator cannot change projects settings, since some of them need remote changes on the Bitbucket website.
  3. If you try to regenerate your Push URL under Deployment Hooks tab, your previous Push to Deploy Project Setting is not updated accordingly, causing the automatic deployments to fail.

Summary

Envoyer looks like an interesting addition to PHP ecosystem which may make some developers happy and satisfied, at least at the first glance.

It's a good tool for not complicated, Laravel-based projects. It's thousands times better than using old-fashion FTP/SFTP to transfer the latest changes into production server.

However the more you work with Envoyer and the more your projects grow, the more shortcomings and drawbacks you become to see. In the end it's just a thin, web-based convenience layer on top of the vast application deployment topic - it imposes certain rules on your development team, so read carefully following points

  • you can add multiple servers, but you cannot export theirs configuration
  • you can hook into four fixed steps of deployment, but you cannot export their configuration either
  • you can only see four latest deployments
  • you cannot decide how many releases of your project Envoyer is about to keep - magic number four from my experience
  • one project equals one repository branch, so you need to keep creating projects if you want to deploy multiple branches of the same repository on the same server
  • you theoretically can have Push to Deploy deployments, but they become unusable when you start using Pull Requests
  • if you use Subversion or have privately hosted git repository (which is not Self-hosted Gitlab), you cannot even start
  • if you don't like Envoyer's url health checks, you can try out Status Cake uptime monitoring tool, which is free and does the job much better

If some of the information are incorrect or you experienced other issues, please let me know via Twitter.