Jose Jimenez
Jose Jimenez
Software Architect & Developer
> >

Prevent Disk Space Woes with Automated Laravel Envoyer Release Cleanup

Published in Laravel, Envoyer, PHP on Jul 31, 2025

Laravel Envoyer is an incredibly powerful tool for zero-downtime deployments. However, if a deployment fails partway through, Envoyer can sometimes leave behind incomplete release directories. Over time, these orphaned directories, combined with the standard accumulation of old releases, can silently eat up disk space, potentially leading to your server running out of room and causing critical system failures.

We've experienced this headache ourselves and developed a robust solution: a pre-deployment hook that automatically cleans up old and orphaned releases, ensuring your server's disk space remains healthy.

The Problem: Accumulating Releases

Envoyer's deployment strategy involves creating new release directories and then symlinking the current directory to the latest successful release. This is great for rollbacks, but it also means that every deployment, successful or not, can leave a new directory in your releases folder. If deployments frequently fail or if you deploy very often, these directories can pile up, consuming valuable disk space.

The Solution: A Smart Cleanup Script

Our solution is a simple yet effective bash script that runs as a "Before Deploy" hook in Envoyer. This script intelligently identifies and removes old releases, while crucially protecting the most recent successful deployments and the currently active release.

Here's how it works:

  1. Identifies the Current Release: It first determines which release your current symlink points to, ensuring that your live application's files are never touched.
  2. Keeps Recent Releases: It then identifies and preserves the four most recent release directories. This provides a safety net for quick rollbacks if needed.
  3. Deletes the Rest: Any other release directories (those older than the four most recent and not the current active one) are then safely removed.
  4. Force Mode for Automation: The script includes a "force" mode (-f) which bypasses the interactive confirmation, making it perfect for automated deployment environments like Envoyer.

Implementing the Cleanup Hook in Envoyer

To implement this solution, you'll add a "Before Deploy" hook in your Envoyer project settings. This ensures the cleanup runs before a new deployment starts, preventing further disk space consumption during a potentially failed deployment.

Here's the full script you'll use:

1cd /var/www/html/envoyer; # Adjust this path to your Envoyer project root
2 
3cat > cleanup_releases.sh << 'EOF'
4#!/bin/bash
5 
6# Envoyer Release Cleanup Script
7# Keeps the newest 4 releases + the currently active release
8# Usage: ./cleanup_releases.sh [--force|-f]
9 
10# !!! IMPORTANT: Adjust these paths to match your Envoyer setup !!!
11RELEASES_DIR="/var/www/html/envoyer/releases" # Path to your 'releases' directory
12CURRENT_LINK="/var/www/html/envoyer/current" # Path to your 'current' symlink
 13 
14# Check for force flag
 15FORCE_MODE=false
16if [[ "$1" == "--force" ]] || [[ "$1" == "-f" ]]; then
17 FORCE_MODE=true
18 echo "Running in force mode (no confirmation prompt)"
 19fi
 20 
21# Check if directories exist
22if [ ! -d "$RELEASES_DIR" ]; then
23 echo "Error: Releases directory not found: $RELEASES_DIR"
24 exit 1
 25fi
 26 
27if [ ! -L "$CURRENT_LINK" ]; then
28 echo "Error: Current symlink not found: $CURRENT_LINK"
29 exit 1
 30fi
 31 
32# Get the currently active release (what current symlink points to)
33CURRENT_RELEASE=$(readlink "$CURRENT_LINK" | xargs basename)
34echo "Currently active release: $CURRENT_RELEASE"
 35 
36# Get all releases sorted by name (which corresponds to timestamps)
37cd "$RELEASES_DIR"
38RELEASES=($(ls -1 | grep -E '^[0-9]{14}$' | sort -r)) # Filters for timestamp-named directories
 39 
40echo "Found ${#RELEASES[@]} total releases"
 41 
42# Keep track of releases to preserve
 43KEEP_RELEASES=()
 44 
45# Always keep the current release
46if [[ " ${RELEASES[@]} " =~ " ${CURRENT_RELEASE} " ]]; then
47 KEEP_RELEASES+=("$CURRENT_RELEASE")
48 echo "Preserving current release: $CURRENT_RELEASE"
 49fi
 50 
51# Keep the newest 4 releases
 52NEWEST_COUNT=0
53for release in "${RELEASES[@]}"; do
54 if [[ $NEWEST_COUNT -lt 4 ]]; then
55 # Only add if not already in the keep list
56 if [[ ! " ${KEEP_RELEASES[@]} " =~ " ${release} " ]]; then
57 KEEP_RELEASES+=("$release")
58 echo "Preserving newest release #$((NEWEST_COUNT + 1)): $release"
59 fi
60 NEWEST_COUNT=$((NEWEST_COUNT + 1))
61 fi
 62done
 63 
64echo ""
65echo "Releases to keep:"
66for keep in "${KEEP_RELEASES[@]}"; do
67 echo " - $keep"
 68done
 69 
70echo ""
71echo "Releases to delete:"
 72DELETE_COUNT=0
73for release in "${RELEASES[@]}"; do
74 if [[ ! " ${KEEP_RELEASES[@]} " =~ " ${release} " ]]; then
75 echo " - $release"
76 DELETE_COUNT=$((DELETE_COUNT + 1))
77 fi
 78done
 79 
80if [ $DELETE_COUNT -eq 0 ]; then
81 echo "No releases to delete."
82 exit 0
 83fi
 84 
85# Ask for confirmation (unless in force mode)
86if [ "$FORCE_MODE" = false ]; then
87 echo ""
88 read -p "Delete $DELETE_COUNT release(s)? (y/N): " -n 1 -r
89 echo
90 if [[ ! $REPLY =~ ^[Yy]$ ]]; then
91 echo "Aborted."
92 exit 0
93 fi
 94else
95 echo ""
96 echo "Force mode: Proceeding with deletion without confirmation..."
 97fi
 98 
99# Delete the releases
100echo "Deleting releases..."
101for release in "${RELEASES[@]}"; do
102 if [[ ! " ${KEEP_RELEASES[@]} " =~ " ${release} " ]]; then
103 echo "Deleting: $release"
104 rm -rf "$RELEASES_DIR/$release"
105 fi
106done
107 
108echo "Cleanup completed!"
109echo "Kept ${#KEEP_RELEASES[@]} releases."
110EOF
111 
112# Make it executable and run in force mode
113chmod +x cleanup_releases.sh;
114./cleanup_releases.sh -f;

Important Considerations:

  • Adjust Paths: The script includes placeholders for RELEASES_DIR and CURRENT_LINK. You must adjust these paths to match your specific Envoyer project setup on your server. Typically, these will be within your /home/envoyer/{project-name}/ directory.
  • Safety First: While this script is designed to be safe, it's always recommended to test it in a staging or development environment first before deploying it to production.
  • Number of Releases: We chose to keep 4 recent releases. You can modify the NEWEST_COUNT variable in the script if you wish to retain more or fewer old releases.

Conclusion

By implementing this simple pre-deployment hook, you can proactively manage your server's disk space, prevent unexpected outages due to full disks, and ensure your Laravel Envoyer deployments run smoothly and efficiently. This small addition can save you from big headaches down the line.

We hope this helps someone else facing similar challenges!