Automating Deployments and Rollbacks with Forge for Zero-Downtime
Introduction
This post outlines how to implement zero-downtime deployments and rollbacks using Laravel Forge. Automating these processes ensures minimal disruption and a smoother experience for users during updates.
Prerequisites
- A Laravel application deployed with Laravel Forge
- Basic understanding of deployment scripts
- SSH access to your server
Step 1: Deployment Script Enhancement
Modify your deployment script in Forge to handle zero-downtime deployments. The key is to activate the new release before deactivating the old one.
Here's a sample snippet:
#!/usr/bin/env bash
# Activate maintenance mode
php artisan down
# Deploy code
git pull origin main
# Install dependencies
composer install --no-interaction --prefer-dist --optimize-autoloader
# Run database migrations
php artisan migrate --force
# Clear cache
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
# Restart queue workers (example)
php artisan queue:restart
# Take site out of maintenance mode
php artisan up
To achieve zero-downtime, the script needs to be adjusted to create a new release directory, deploy the code there, and then symlink it to the current live directory. Error handling is also important.
#!/usr/bin/env bash
# Define release directory
RELEASE_DIR="releases/$(date +%Y%m%d%H%M%S)"
# Create release directory
mkdir -p "$RELEASE_DIR"
# Clone repository into release directory
git clone --depth 1 -b main . "$RELEASE_DIR"
# Install dependencies in release directory
cd "$RELEASE_DIR"
composer install --no-interaction --prefer-dist --optimize-autoloader
# Run database migrations
php artisan migrate --force
# Clear cache
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
# Activate the new release by updating the symlink
ln -nfs "$RELEASE_DIR/public" current
# Restart queue workers (example)
php artisan queue:restart
# Remove old releases (keep last 5)
cd releases
ls -t | tail -n +6 | xargs rm -rf
echo "Deployment completed successfully!"
Step 2: Rollback Script Implementation
Create a rollback script that reverts to the previous release if a deployment fails. This involves switching the symlink back to the previous directory.
#!/usr/bin/env bash
# Get the two latest release directories
RELEASES=$(ls -t releases | head -n 2)
# Check if there are at least two releases
if [[ -z "$RELEASES" || $(echo "$RELEASES" | wc -w) -lt 2 ]]; then
echo "Not enough releases to rollback."
exit 1
fi
# Extract the previous release directory
PREVIOUS_RELEASE=$(echo "$RELEASES" | tail -n 1)
# Check if the previous release directory exists
if [ ! -d "releases/$PREVIOUS_RELEASE" ]; then
echo "Previous release directory not found: releases/$PREVIOUS_RELEASE"
exit 1
fi
# Switch the 'current' symlink back to the previous release
ln -nfs "releases/$PREVIOUS_RELEASE/public" current
echo "Rollback to previous release completed."
Step 3: Configure Forge
In your Forge deployment settings, add the deployment and rollback scripts. Ensure they are executable.
Results
Implementing these scripts provides a robust zero-downtime deployment and rollback strategy, minimizing service interruptions.
Next Steps
Enhance the scripts further by adding health checks after deployment, or implement more sophisticated rollback strategies based on application monitoring metrics. Consider integrating with a monitoring service like New Relic to automate rollbacks based on error rates.