This tutorial demonstrates how anyone can implement zero-downtime deployments when using Forge without using Laravel Envoyer. To do that we will use Laravel Envoy, a tool that helps setup tasks during deployment using Blade syntax.
Note
If you need advanced features (like multi-server support), a beautiful UI, or a hassle-free deployment experience, I highly recommend trying out Laravel Envoyer. Envoyer offers a wide range of features that can streamline your deployment process and provide a user-friendly interface
To start, you’ll need Envoy installed globally on your production server. To do that simply run composer global require laravel/envoy
.
After this is completed, you need to create your Envoy.blade.php file in the root of your project and have it committed into git. As a starting point you may use the following file, which should cover the most common deployments in a typical laravel project.
@servers(['production' => '127.0.0.1'])
@setup
$repository = '[email protected]:yourorganization/yourprojectname.git';
$releasesPath = '/home/forge/yoursitename/releases';
$siteRootDirectory = '/home/forge/yoursitename';
$release = date('YmdHis');
$newReleaseDirectory = $releasesPath .'/'. $release;
@endsetup
@story('deploy')
build
activate
@endstory
@task('build')
echo 'Cloning repository'
git clone --depth 1 {{ $repository }} {{ $newReleaseDirectory }}
echo 'Enter New Release directory'
cd {{ $newReleaseDirectory }}
@if ($commit)
echo 'Switching to specific commit with git reset --hard {{ $commit }}'
git reset --hard {{ $commit }}
@endif
echo 'Install composer dependencies'
composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist --no-dev --optimize-autoloader
php artisan optimize:clear
echo 'Linking .env file'
ln -nfs {{ $siteRootDirectory }}/.env {{ $newReleaseDirectory }}/.env
npm i
npm run prod
@endtask
@task('activate', ['on' => 'production'])
echo "Linking storage directory"
rm -rf {{ $newReleaseDirectory }}/storage
ln -nfs {{ $siteRootDirectory }}/storage {{ $newReleaseDirectory }}/storage
cd {{ $newReleaseDirectory }}
php artisan optimize
php artisan migrate --force
php artisan storage:link
php artisan nova:publish
php artisan horizon:publish
echo 'Linking current release'
ln -nfs {{ $newReleaseDirectory }} {{ $siteRootDirectory }}/current
php artisan horizon:terminate
( flock -w 10 9 || exit 1
echo 'Restarting FPM...'; sudo -S service php8.2-fpm reload ) 9>/tmp/fpmlock
@endtask
After that you will need to make a few changes to your Laravel Forge site to use the above file when deploying.
Step 1: Update your Web Directory to /current/public
. The current
symlink will be updated at the end of each deployment, and will always be pointing to last successful release.
Step 2: Update your Forge deployment script to trigger Envoy.
cd /home/forge/yoursitename/current
$FORGE_PHP /home/forge/.config/composer/vendor/bin/envoy run deploy
Step 3: If your site is already deployed you will need to move to do some manual work to move the files into the structure we need.
Inside your /home/forge/yoursitename
directory you should have the following files/directories.
- Your laravel’s .env file
- Your auth.json (optional – needed only if you are pulling private packages like Nova)
- The releases folder where all of your deployments will be stored
- Your storage folder (which is symlinked during deployment)
After this, you will need to create the first symlink, since it is needed for the first actual deployment through Forge to happen.
ln -nfs /home/forge/yoursitename/releases/yourreleasetimestamp /home/forge/yoursitename/current
Your are all set! Let me know how it went!