Laravel – Envoy

Laravel Envoy is an official package that allows you to execute tasks on remote servers. Envoy file allows preparing an entire set of commands which later can be called manually or with CI/CD.

Envoy Setup

Envoy file should be placed in root directory of your project and named Envoy.blade.php. Envoy uses blade structure to define tasks, stories, servers and other structures. The task is defined by the programmer, and its functionality can be as wide as they need to be. They can call artisan commands or even system commands as long as the remote server user has privileges to these actions. Combining Laravel Envoy with CI/CD of your project management tools can provide a fully automatic deployment process.

@servers(['vps' => '<remote_server_user>@<remote_server_address>'])

@setup
$repository = '<repository_url>';
$environment = ($branch === '<main-branch-name>' ? 'production' : 'develop');
$root_dir = '/var/www/example-app';
$app_dir = $root_dir . '/' . $environment;
$releases_dir = $app_dir . '/releases';
$release = date('YmdHis');
$new_release_dir = $releases_dir .'/'. $release;
$current_release_dir = $app_dir . '/current';
@endsetup

@story('deploy')
backup_database
clone_repository
run_composer
update_symlinks
clear_cache_directory
optimize_application
migrate_database
seed_database
link_storage
symlink_current_release
delete_old_releases
@endstory

@task('clone_repository')
echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 --single-branch --branch {{ $branch }} {{ $repository }} {{ $new_release_dir }}
cd {{ $new_release_dir }}
git reset --hard {{ $commit }}
@endtask

@task('run_composer')
echo "Starting deployment ({{ $release }})"
cd {{ $new_release_dir }}
composer install --prefer-dist --no-scripts -q -o
@endtask

@task('update_symlinks')
echo "Linking storage directory"
rm -rf {{ $new_release_dir }}/storage
ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage

echo "Linking bootstrap/cache directory"
rm -rf {{ $new_release_dir }}/bootstrap/cache
ln -nfs {{ $app_dir }}/bootstrap/cache {{ $new_release_dir }}/bootstrap/cache

echo 'Linking .env file'
ln -nfs {{ $root_dir }}/.env.{{ $environment }} {{ $new_release_dir }}/.env
@endtask

@task('clear_cache_directory')
echo "Clearing bootstrap/cache directory content"
rm -f {{ $app_dir }}/bootstrap/cache/*
@endtask

@task('optimize_application')
echo "Optimizing application"
cd {{ $new_release_dir }}
php artisan optimize
@endtask

@task('migrate_database')
echo "Migrating database"
cd {{ $new_release_dir }}
php artisan migrate --force
@endtask

@task('seed_database')
echo "Seeding database"
cd {{ $new_release_dir }}
php artisan db:seed --force
@endtask

@task('symlink_current_release')
echo 'Linking current release'
ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
@endtask

@task('delete_old_releases')
echo 'Deleting old releases'
rm -rf `ls -dt {{ $releases_dir }}/* | tail -n +4`;
@endtask

@task('link_storage')
echo 'Linking storage'
cd {{ $new_release_dir }}
php artisan storage:link
@endtask

@task('backup_database')
echo 'Creating backup of database'
cd {{ $current_release_dir }}
php artisan db:dump
@endtask

Servers and Setup

Server structure is used to define multiple servers on which your application is hosted. For example, if you want to separate environments of your application to different server then you can define your server like this:

@servers(['vps_dev' => ['user@192.168.1.1'], 'vps_prod' => ['user@192.168.1.2']])

You can condition your tasks to execute only on specific servers. By modifying task backup_database, we can restrict creating database backups only to production environment.

@task('backup_database', ['on' => 'vps_prod'])
echo 'Creating backup of database'
cd {{ $current_release_dir }}
php artisan db:dump
@endtask

Setup structure allows to initiate variables. You can use PHP code while initiating a variable. For example variables can be later used to point to other directories on your server. Or they can define values which are later used as a part of commands. You can save your branch names to variables to dynamically change from which branch deploy your code.

Variables can be passed from CLI to your Envoy.

php vendor/bin/envoy run deploy --branch=<main-branch-name>

Tasks

Task structure is a set of commands executed one after another. Tasks can perform various actions, but for readability of your code they should be separated into smaller instances. Defining one big task which deploys an entire application can be done but modifying this kind of code can be problematic in the future. Separating the deployment process into smaller tasks can be easier maintained for cases when you wish to condition some behaviors or add another step between already existing ones.

Story

Story structure is a list of tasks. Order of tasks in story matter because they are executed one after another. Separating tasks into smaller instances can be beneficial due to this story structure. Adding another part of code somewhere in between deployment process is as easy as including a new position in story list. You can condition your story to exclude or include various tasks.

Summary

Laravel Envoy is a powerful tool which allows executing various tasks on remote servers. In this example, I focused purely on deployment of your application to the server, but Laravel Envoy can be used for different actions too.

More about Laravel Envoy can be read in official Laravel Envoy documentation.