laravel-deploy
Auto-invoked skill
Configure deployment with zero-downtime, health checks
Trigger Keywords
This skill automatically activates when Claude detects these keywords:
deploy
production
forge
vapor
envoy
zero-downtime
Overview
The laravel-deploy skill provides expertise for deploying Laravel applications to production. It covers zero-downtime deployments, health checks, environment configuration, and platform-specific setups for Forge, Vapor, and custom servers.
What This Skill Provides
- Zero-Downtime - Atomic deployments with symlinks
- Health Checks - Endpoint monitoring and rollback triggers
- Platform Setup - Forge, Vapor, Ploi, custom VPS
- Environment Config - Production-ready settings
- Caching - Config, route, view, and event caching
- Queue Management - Horizon/worker restart strategies
Example Conversations
# Zero-downtime setup
"Set up zero-downtime deployment with Envoy"
# Health checks
"Add a health check endpoint for my load balancer"
# Production optimization
"What caching commands should I run after deployment?"
# Platform-specific
"Configure my Laravel Forge deployment script"
Health Check Endpoint
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class HealthController extends Controller
{
public function __invoke()
{
$checks = [
'status' => 'ok',
'timestamp' => now()->toISOString(),
'checks' => [
'database' => $this->checkDatabase(),
'cache' => $this->checkCache(),
'redis' => $this->checkRedis(),
],
];
$allPassing = collect($checks['checks'])
->every(fn ($check) => $check['status'] === 'ok');
return response()->json($checks, $allPassing ? 200 : 503);
}
private function checkDatabase(): array
{
try {
DB::select('SELECT 1');
return ['status' => 'ok'];
} catch (\Exception $e) {
return ['status' => 'fail', 'error' => $e->getMessage()];
}
}
private function checkCache(): array
{
try {
Cache::put('health-check', true, 10);
Cache::forget('health-check');
return ['status' => 'ok'];
} catch (\Exception $e) {
return ['status' => 'fail', 'error' => $e->getMessage()];
}
}
private function checkRedis(): array
{
try {
Redis::ping();
return ['status' => 'ok'];
} catch (\Exception $e) {
return ['status' => 'fail', 'error' => $e->getMessage()];
}
}
}
// routes/web.php
Route::get('/health', HealthController::class);
Zero-Downtime Deploy Script
#!/bin/bash
# deploy.sh - Zero-downtime deployment
set -e
REPO="git@github.com:user/repo.git"
RELEASES_DIR="/var/www/app/releases"
SHARED_DIR="/var/www/app/shared"
CURRENT_LINK="/var/www/app/current"
RELEASE=$(date +%Y%m%d%H%M%S)
# Clone fresh release
git clone --depth 1 $REPO "$RELEASES_DIR/$RELEASE"
cd "$RELEASES_DIR/$RELEASE"
# Link shared files
ln -nfs "$SHARED_DIR/.env" .env
ln -nfs "$SHARED_DIR/storage" storage
# Install dependencies
composer install --no-dev --optimize-autoloader
# Build assets
npm ci && npm run build
# Cache everything
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
# Run migrations
php artisan migrate --force
# Atomic switch (zero-downtime moment)
ln -nfs "$RELEASES_DIR/$RELEASE" "$CURRENT_LINK"
# Restart workers
php artisan queue:restart
php artisan horizon:terminate 2>/dev/null || true
# Cleanup old releases (keep last 5)
ls -dt "$RELEASES_DIR"/* | tail -n +6 | xargs rm -rf
echo "Deployed release: $RELEASE"
Laravel Forge Deploy Script
# Forge deployment script
cd /home/forge/example.com
git pull origin $FORGE_SITE_BRANCH
$FORGE_COMPOSER install --no-dev --no-interaction --prefer-dist --optimize-autoloader
( flock -w 10 9 || exit 1
echo 'Restarting FPM...'; sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock
if [ -f artisan ]; then
$FORGE_PHP artisan migrate --force
$FORGE_PHP artisan config:cache
$FORGE_PHP artisan route:cache
$FORGE_PHP artisan view:cache
$FORGE_PHP artisan queue:restart
fi
npm ci
npm run build
Production Environment
# .env.production essentials
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com
# Performance
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
# Security
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE_COOKIE=strict
# Logging
LOG_CHANNEL=stack
LOG_LEVEL=error
# Database pooling
DB_POOL_MIN=2
DB_POOL_MAX=10
Optimization Commands
| Command | Purpose |
|---|---|
config:cache |
Cache all config files into one |
route:cache |
Cache route registrations |
view:cache |
Pre-compile all Blade templates |
event:cache |
Cache event-listener mappings |
optimize |
Run all cache commands at once |
Common Pitfalls
- Forgetting
queue:restart- Workers cache old code - Missing
--force- Migrations won't run in production without it - APP_DEBUG=true - Exposes sensitive error details
- No health checks - Load balancers need endpoints to verify health
Rollback Strategy
#!/bin/bash
# rollback.sh - Quick rollback to previous release
RELEASES_DIR="/var/www/app/releases"
CURRENT_LINK="/var/www/app/current"
# Get previous release
PREVIOUS=$(ls -t "$RELEASES_DIR" | sed -n '2p')
if [ -z "$PREVIOUS" ]; then
echo "No previous release found"
exit 1
fi
# Atomic switch back
ln -nfs "$RELEASES_DIR/$PREVIOUS" "$CURRENT_LINK"
php artisan queue:restart
echo "Rolled back to: $PREVIOUS"
Related Commands
- /laravel-agent:deploy:setup - Configure deployment
- /laravel-agent:cicd:setup - Setup CI/CD pipeline
Related Agent
- laravel-deploy - Deployment specialist