| name | olakunlevpn-deployment-skills |
| description | Use when setting up deployment scripts, configuring auto-deploy, creating cron jobs, or deploying Laravel and Inertia applications to production servers. Generates deploy-pull.sh and auto-deploy.sh scripts with proper deployment sequence, logging, error handling, and cron configuration. |
Laravel Deployment Scripts
Generate production-ready deployment scripts for Laravel + Inertia applications. Two scripts: manual deploy and auto-deploy with GitHub polling. Always ask for the project path and log path before generating.
Rules
- ALWAYS ask for the server project directory path and log directory path before generating scripts. These are SERVER paths, not local machine paths. Every server is different.
- NEVER hardcode paths, usernames, domains, or server-specific values. The scripts use configurable variables at the top that the user sets once for their server.
- NEVER skip migration. Always include
php artisan migrate --force --no-interaction.
- ALWAYS clear and rebuild cache.
optimize:clear then optimize in that order.
- ALWAYS stash local changes before pulling. Prevents merge conflicts on server.
- ALWAYS include error handling. Scripts should exit on failure with clear error messages.
- NEVER run
npm run dev in production. Always npm run build.
- ALWAYS log deployments with timestamps.
- Auto-deploy checks every 2 minutes by default. Configurable.
- Both scripts must be in the project root and added to
.gitignore.
Phase 1: Gather Information
Before generating anything, you MUST ask the user these questions. Do NOT assume any answers. Do NOT generate scripts until all required answers are collected.
ASK THESE QUESTIONS (one message, wait for all answers):
1. "What is the full server path to your project?"
Example: /home/ubuntu/htdocs/example.com
This is the path ON THE SERVER, not your local machine.
2. "Do you need npm install and npm run build in the deploy?"
- YES = project has frontend assets (React, Vue, Vite, etc.)
- NO = backend-only project, no frontend build step
3. "Do you need composer install in the deploy?"
- YES = always run composer install on deploy
- AUTO = only run when composer.lock changes (recommended)
- NO = skip composer entirely
4. "What git branch do you deploy from?" (default: main)
5. "Does your project use any of these? (list all that apply)"
- Filament (adds filament:optimize + icons:cache)
- Queue workers (adds queue:restart)
- Horizon (adds horizon:terminate)
- Reverb/WebSockets (adds reverb:restart)
- None of the above
Do NOT proceed until the user answers questions 1, 2, and 3. These are mandatory.
Questions 4 and 5 have sensible defaults (main, none) but still ask.
After collecting answers, generate scripts tailored to their exact setup. If they said no npm, the script has no npm steps. If they said no composer, no composer steps. Only include what they need.
Phase 2: Generate Scripts
Script 1: deploy-pull.sh (Manual Deploy)
Generate this script based on the user's answers. Only include the steps they need.
Template (adapt based on answers):
#!/bin/bash
set -e
BRANCH="main"
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_DIR="${LOG_DIR:-$PROJECT_DIR/storage/logs}"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/deploy.log"
exec > >(tee -a "$LOG_FILE") 2>&1
echo ""
echo "=== Deploy started at $TIMESTAMP ==="
cd "$PROJECT_DIR"
echo "[1/N] Stashing local changes..."
git stash 2>/dev/null || true
echo "[2/N] Pulling from $BRANCH..."
git pull origin "$BRANCH"
if git diff HEAD@{1} --name-only 2>/dev/null | grep -q "composer.lock"; then
echo "[3/N] Installing PHP dependencies..."
composer install --no-dev --no-interaction --optimize-autoloader
else
echo "[3/N] No composer changes, skipping..."
fi
echo "[4/N] Installing npm packages..."
npm install --legacy-peer-deps 2>/dev/null
echo "[5/N] Building frontend assets..."
npm run build
echo "[6/N] Running migrations..."
php artisan migrate --force --no-interaction
echo "[7/N] Optimizing..."
php artisan optimize:clear
php artisan optimize
echo "=== Deploy completed at $(date '+%Y-%m-%d %H:%M:%S') ==="
echo ""
Adjust step numbers [1/N] based on how many steps are included.
- No npm + no composer = fewer steps
- All features = more steps
- Always count accurately. Never show [4/7] when there are only 5 steps.
Script 2: auto-deploy.sh (Auto Deploy via Cron)
Same rules apply -- only include steps the user asked for.
#!/bin/bash
set -e
BRANCH="main"
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_DIR="${LOG_DIR:-$PROJECT_DIR/storage/logs}"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/auto-deploy.log"
cd "$PROJECT_DIR"
git fetch origin "$BRANCH" --quiet
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse "origin/$BRANCH")
if [ "$LOCAL" = "$REMOTE" ]; then
exit 0
fi
echo "" >> "$LOG_FILE"
echo "=== Auto-deploy started at $(date '+%Y-%m-%d %H:%M:%S') ===" >> "$LOG_FILE"
echo "Local: $LOCAL" >> "$LOG_FILE"
echo "Remote: $REMOTE" >> "$LOG_FILE"
exec >> "$LOG_FILE" 2>&1
git stash 2>/dev/null || true
git pull origin "$BRANCH"
php artisan migrate --force --no-interaction
php artisan optimize:clear
php artisan optimize
echo "=== Auto-deploy completed at $(date '+%Y-%m-%d %H:%M:%S') ===" >> "$LOG_FILE"
Phase 3: Setup Instructions
After generating scripts, provide setup steps using the ACTUAL paths the user gave you. Never use placeholders in the final output.
Step 1 — Make executable:
chmod +x /path/user/gave/deploy-pull.sh
chmod +x /path/user/gave/auto-deploy.sh
Step 2 — Add to .gitignore:
echo "deploy-pull.sh" >> .gitignore
echo "auto-deploy.sh" >> .gitignore
Step 3 — Add cron jobs:
crontab -e
Add these lines (use the ACTUAL server path the user provided):
*/2 * * * * /bin/bash /path/user/gave/auto-deploy.sh 2>&1
* * * * * cd /path/user/gave && php artisan schedule:run >> /dev/null 2>&1
Step 4 — Verify:
crontab -l
bash /path/user/gave/deploy-pull.sh
tail -f /path/user/gave/storage/logs/auto-deploy.log
By default, logs go to storage/logs/ inside the project. If the user wants logs elsewhere, they set LOG_DIR env variable:
LOG_DIR=/var/log/myapp bash deploy-pull.sh
Phase 4: Variations
For projects with queue workers
Add after the optimize step:
php artisan queue:restart
For projects with Horizon
Add after the optimize step:
php artisan horizon:terminate
Supervisor will auto-restart Horizon.
For projects with Filament
Add after the optimize step:
php artisan filament:optimize
php artisan icons:cache
For projects with scheduled tasks
Ensure this cron exists:
* * * * * cd [PROJECT_DIR] && php artisan schedule:run >> /dev/null 2>&1
For projects with Reverb (WebSockets)
Add after the optimize step:
php artisan reverb:restart
For projects needing Composer install
Only run when composer.lock changed:
if git diff HEAD@{1} --name-only | grep -q "composer.lock"; then
composer install --no-dev --no-interaction --optimize-autoloader
fi
For projects with specific PHP version
Replace php with the full path:
/usr/bin/php8.3 artisan migrate --force --no-interaction
For multiple environments (staging + production)
Create separate scripts:
deploy-staging.sh → pulls from develop branch
deploy-production.sh → pulls from main branch
For Webhook-based deploy (instead of polling)
Instead of cron polling, use a GitHub webhook that hits a deploy URL:
Route: POST /deploy-webhook
Secret: validated via X-Hub-Signature-256 header
Action: Runs deploy-pull.sh
Deployment Sequence (Strict Order)
This order matters. Never rearrange.
1. git stash → Protect local changes
2. git pull → Get latest code
3. composer install → PHP dependencies (if changed)
4. npm install → JS dependencies
5. npm run build → Build frontend assets
6. php artisan migrate → Database changes
7. php artisan optimize:clear → Clear ALL caches
8. php artisan optimize → Rebuild caches
9. [Service restarts] → Queue, Horizon, Reverb, etc.
Why this order:
- Stash before pull prevents merge conflicts
- Composer before npm because PHP may generate assets npm needs
- npm install before build because build needs all packages
- Build before migrate because sometimes migrations reference frontend
- Clear before optimize to ensure fresh cache state
- Service restarts LAST because they need the new code + cache
Monitoring Commands
tail -f storage/logs/auto-deploy.log
tail -50 storage/logs/deploy.log
crontab -l
grep "Deploy completed" storage/logs/auto-deploy.log | tail -1
cd [PROJECT_DIR] && git log --oneline -1
cd [PROJECT_DIR] && git fetch && git log HEAD..origin/main --oneline
crontab -e
bash [PROJECT_DIR]/deploy-pull.sh
How to Use
Set up deployment for a new project:
Set up deployment scripts for my project at /home/user/htdocs/myapp.com
Logs should go to /home/user/logs
Set up with extras:
Set up deployment for /var/www/myapp with Filament, queues, and Horizon.
Log to /var/log/myapp
Fix a failed deploy:
My auto-deploy failed. Check the log and fix the issue.
Add deploy to existing project:
Add deploy-pull.sh and auto-deploy.sh to this project.
My server path is /home/user/htdocs/example.com
Switch from polling to webhook:
Replace the cron-based auto-deploy with a GitHub webhook.