Zero Downtime Deployments With Laravel Forge
May 21, 2017 · ⏱4 min read
Laravel Forge is a fantastic tool for rapidly spinning up servers with all the best and latest software. It also provides for easy PHP project deployment and setup. However, the default deploy script is rather lacking in a sense that it results in downtime.
Envoyer is a tool that solves this issue, but if you just want zero downtime deployments and don't want any of the other features that it provides, it might be hard to justify shelling out for another tool.
Here's a way to handle zero downtime deployments from Forge, in a very similar manner to Envoyer.
Note: the default
Forge project is used as an example throughout this post. Remember to change it to whatever your project domain name is set to in Forge.
Deploying
The below script should go into the Deploy Script section of your pre-existing Forge website.
cd /home/forgePROJECT_NAME="default"PROJECT_REPO="git@github.com:laravel/laravel.git"RELEASES_KEPT=3RELEASE=$(date +"%Y-%m-%d-%H-%M-%S")# Copy the old .env filecp $PROJECT_NAME"/.env" $PROJECT_NAME".env"# Create a default-releases directory if it does not exist.if [ ! -d $PROJECT_NAME"-releases" ]thenmkdir $PROJECT_NAME"-releases"fi# Create our new release.cd $PROJECT_NAME"-releases"git clone $PROJECT_REPO $RELEASEcd $RELEASE# Install dependenciescomposer install --no-interaction --prefer-dist --optimize-autoloader# Move in the old .env file and clean up.cp -af "../../"$PROJECT_NAME".env" ".env"rm "../../"$PROJECT_NAME".env"if [ -f artisan ]thenphp artisan migrate --forcefi# Clean up old releases.cd ..rm -rf `ls -1 | sort -r | tail -n +$((RELEASES_KEPT+1))`cd /home/forge# Remove the project directory if exists.if [ -d $PROJECT_NAME ]thenrm -rf $PROJECT_NAMEfi# Relink our new release.ln -sfn $PROJECT_NAME"-releases/"$RELEASE $PROJECT_NAME# Reload PHP-FPMecho "" | sudo -S service php7.1-fpm reload
Configuring
There are 4 variables defined at the beginning of the script: PROJECT_NAME
, PROJECT_REPO
, RELEASES_KEPT
and RELEASE
.
PROJECT_NAME
- should be set to the domain name that you have given your website in Forge.
PROJECT_REPO
- is the git repository with your project files. Should be the same repo that you provided when setting up your website for the first time. This must bet an SSH link and not HTTPS.
RELEASES_KEPT
- the number of releases that you want to keep present. These will be used for rolling back, should your freshly deployed code not work as intended.
RELEASE
- the naming pattern for naming your releases. By default, it's the deployment date separated by hyphens.
First Deploy
The very first deployment will introduce a bit of downtime as it has to remove the directory of your project and replace it with a symlink.
Which leads into another point...
Changing the storage path
Since we're essentially using a fresh copy of code every time we deploy, the storage/
folder will not have any of the files that the app has added during its release lifetime.
storage/
usually holds some of the frameworks cache stuff, logs and other tidbits. In most cases, you'll want to keep this folder untouched whenever you do a release.
This issue can be solved by changing your storage/
path to be outside of your PROJECT_NAME
directory. So something like /home/forge/default-storage
would work.
To do this, SSH into your Forge server and run the following to create the new storage folder:
cp -rp /home/forge/default/storage /home/forge/default-storage
Now open up your bootstrap/app.php
and just before return $app;
add this:
$app->bind('path.storage', function() {return env('APP_STORAGE');});
The last step is to add APP_STORAGE=/home/forge/default-storage
to your environment variables.
Rolling Back
Rolling back is just symlinking to an older release and then rolling back your database changes. You can do this by SSHing into your server and running the following:
ln -sfn default-releases/ROLLBACK_RELEASE_FOLDER default
And then roll back any migrations from the previous release:
default/artisan migrate:rollback
Replace the ROLLBACK_RELEASE_FOLDER
with whatever version of your application you want to go back to.
Ending Notes
I don't want to take away anything from Envoyer with this. It is a fantastic tool and if you need more control over your deployments, you should probably use it.
If you find any issues with this post, or would like to add anything to it, please get in touch via the socials on this site and let me know.