Deployment Hooks
Deployment hooks allow you to run custom scripts at specific points in your application's deployment lifecycle. This is essential for database migrations, cache warming, notifications, and other automated tasks.
Overview
Flux-Orbit supports deployment hooks through executable bash scripts placed in your repository root. These hooks run automatically during deployments and updates.
Available Hooks
| Hook File | When It Runs | Use Cases | Failure Impact |
|---|---|---|---|
pre-deploy.sh | After dependencies install, before build | Database migrations, schema updates | Triggers rollback |
post-deploy.sh | After application starts successfully | Cache warming, notifications, cleanup | Logged but non-fatal |
Quick Start
1. Create Hook Scripts
Create executable bash scripts in your repository root:
pre-deploy.sh (for database migrations):
#!/bin/bash
set -e
echo "Running database migrations..."
npx prisma migrate deploy
post-deploy.sh (for notifications):
#!/bin/bash
set -e
echo "Deployment complete! Sending notification..."
curl -X POST https://hooks.slack.com/services/YOUR/WEBHOOK/URL \
-H 'Content-Type: application/json' \
-d '{"text":"🚀 App deployed successfully!"}'
2. Make Scripts Executable
chmod +x pre-deploy.sh post-deploy.sh
3. Commit and Deploy
git add pre-deploy.sh post-deploy.sh
git commit -m "Add deployment hooks"
git push
That's it! Your hooks will run automatically on the next deployment.
Hook Execution Order
Initial Deployment
1. Clone repository
2. Detect project type
3. Install runtime (Node.js/Python/Ruby)
4. Install dependencies
↓
5. Run pre-deploy.sh ← Hook runs here
↓
6. Build application
7. Start application
8. Health check (60s)
↓
9. Run post-deploy.sh ← Hook runs here
CI/CD Updates (Webhooks/Polling)
1. Git pull latest code
2. Check if dependencies changed
3. Install/update dependencies if needed
↓
4. Run pre-deploy.sh ← Hook runs here
↓
5. Build application (if needed)
6. Stop current app
7. Start new version
8. Health check (60s)
↓
9. Run post-deploy.sh ← Hook runs here
If any step fails (including pre-deploy hook), the deployment rolls back to the previous working version.
Hook Requirements
Must Have
- ✅ Executable permission:
chmod +x pre-deploy.sh - ✅ Shebang line: Must start with
#!/bin/bash - ✅ Exit codes: Exit 0 for success, non-zero for failure
- ✅ Location: Repository root (or
PROJECT_PATHif using monorepos)
Best Practices
- ✅ Use
set -e: Exit immediately on any error - ✅ Be idempotent: Safe to run multiple times
- ✅ Add logging: Echo progress messages
- ✅ Handle secrets: Use environment variables, not hardcoded values
- ✅ Test locally: Run scripts manually before deploying
Real-World Examples
Node.js with Prisma Migrations
#!/bin/bash
# pre-deploy.sh
set -e
echo "=========================================="
echo "Running Prisma Migrations"
echo "=========================================="
# Generate Prisma client
npx prisma generate
# Run migrations
npx prisma migrate deploy
echo "Migrations completed successfully"
Django with PostgreSQL
#!/bin/bash
# pre-deploy.sh
set -e
echo "=========================================="
echo "Running Django Migrations"
echo "=========================================="
# Run database migrations
python manage.py migrate --noinput
# Collect static files
python manage.py collectstatic --noinput
echo "Django setup completed"
Rails with Redis Cache
#!/bin/bash
# pre-deploy.sh
set -e
echo "=========================================="
echo "Running Rails Migrations"
echo "=========================================="
# Run migrations
bundle exec rails db:migrate
# Clear cache to avoid stale data
bundle exec rails runner "Rails.cache.clear"
echo "Rails setup completed"
Post-Deploy: Slack Notification
#!/bin/bash
# post-deploy.sh
set -e
COMMIT_SHA=$(git rev-parse --short HEAD)
DEPLOY_TIME=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
curl -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{
\"text\": \"🚀 Deployment Successful\",
\"attachments\": [{
\"color\": \"good\",
\"fields\": [
{\"title\": \"Commit\", \"value\": \"$COMMIT_SHA\", \"short\": true},
{\"title\": \"Time\", \"value\": \"$DEPLOY_TIME\", \"short\": true}
]
}]
}"
echo "Notification sent to Slack"
Post-Deploy: Cache Warming
#!/bin/bash
# post-deploy.sh
set -e
echo "Warming up application caches..."
# Make requests to key endpoints
curl -s http://localhost:${APP_PORT}/api/popular-items >/dev/null
curl -s http://localhost:${APP_PORT}/api/categories >/dev/null
curl -s http://localhost:${APP_PORT}/api/featured >/dev/null
echo "Cache warming completed"
Environment Variables
Hook Control
SKIP_HOOKS: "true" # Disable all hooks for this deployment
HOOK_TIMEOUT: "600" # Hook timeout in seconds (default: 300)
Database Connections
# PostgreSQL
DATABASE_URL: "postgresql://user:pass@host:5432/dbname"
# MySQL
DATABASE_URL: "mysql://user:pass@host:3306/dbname"
# MongoDB
MONGODB_URI: "mongodb://user:pass@host:27017/dbname"
# Redis
REDIS_URL: "redis://host:6379"
Notification URLs
SLACK_WEBHOOK_URL: "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
DISCORD_WEBHOOK_URL: "https://discord.com/api/webhooks/YOUR/WEBHOOK"
Monorepo Support
If you're using PROJECT_PATH to deploy a subfolder, hooks can be placed in two locations:
Option 1: Subfolder Hooks (Recommended)
monorepo/
├── apps/
│ └── frontend/
│ ├── package.json
│ ├── pre-deploy.sh ← Runs for this app
│ └── post-deploy.sh
└── backend/
Option 2: Root Hooks (Fallback)
monorepo/
├── pre-deploy.sh ← Runs if subfolder hook not found
├── post-deploy.sh
└── apps/
└── frontend/
└── package.json
Flux-Orbit checks the subfolder first, then falls back to root if not found.
Error Handling
Pre-Deploy Hook Failures
If pre-deploy.sh exits with a non-zero code:
- ❌ Deployment stops immediately
- 🔄 Automatic rollback to previous version
- 📝 Error logged to
/app/logs/hooks.log - 🚫 Application does not restart
Example failure:
#!/bin/bash
set -e
# This will fail if migrations have conflicts
npx prisma migrate deploy
# If the above fails (exit code != 0):
# - Deployment stops here
# - App rolls back to previous commit
# - Current version keeps running
Post-Deploy Hook Failures
If post-deploy.sh exits with a non-zero code:
- ⚠️ Failure is logged as WARNING
- ✅ Deployment continues (app keeps running)
- 📝 Error logged to
/app/logs/hooks.log - 🔄 No rollback triggered
Example (non-fatal):
#!/bin/bash
set -e
# This might fail (e.g., Slack is down)
curl -X POST "$SLACK_WEBHOOK_URL" -d '{"text":"Deployed"}'
# If the above fails:
# - Warning is logged
# - App continues running
# - Deployment is still successful
Hook Timeout
Hooks have a 5-minute timeout by default:
# If your hook takes longer than 5 minutes:
HOOK_TIMEOUT=900 # Increase to 15 minutes
When a hook times out:
- ❌ Treated as failure
- 🔄 Pre-deploy timeout triggers rollback
- ⚠️ Post-deploy timeout is logged but non-fatal
Troubleshooting
Hook Not Running
Problem: Hook exists but doesn't execute
Solutions:
# 1. Check if executable
ls -la pre-deploy.sh
# Should show: -rwxr-xr-x (x = executable)
# 2. Make executable
chmod +x pre-deploy.sh
# 3. Verify shebang
head -1 pre-deploy.sh
# Should output: #!/bin/bash
# 4. Check git permissions
git ls-files --stage pre-deploy.sh
# Should show: 100755 (executable in git)
# 5. Update git permissions if needed
git update-index --chmod=+x pre-deploy.sh
git commit -m "Make pre-deploy.sh executable"
Hook Fails with "Command Not Found"
Problem: Hook can't find commands like npx, python, bundle
Cause: PATH not set up or dependencies not installed
Solutions:
#!/bin/bash
set -e
# For Node.js: Use full path or ensure nvm is loaded
# The runtime is already installed and available
npx prisma migrate deploy # This should work
# For Python: Use python/python3
python manage.py migrate
# For Ruby: Use bundle
bundle exec rails db:migrate
Database Connection Fails
Problem: Hook can't connect to database
Solutions:
- Ensure
DATABASE_URLis set:
# In Flux app configuration
DATABASE_URL: postgresql://user:pass@host:5432/db
- Test connection in hook:
#!/bin/bash
set -e
if [ -z "$DATABASE_URL" ]; then
echo "ERROR: DATABASE_URL not set"
exit 1
fi
echo "Database URL: ${DATABASE_URL%%:*}://***" # Don't log password
npx prisma migrate deploy
- Check network access:
- Ensure database host is accessible from Flux network
- Check firewall rules
- Verify credentials
Hook Works Locally but Fails in Production
Problem: Hook runs fine locally but fails in Flux-Orbit
Common causes:
- Missing environment variables:
# Add debug logging
echo "DATABASE_URL: ${DATABASE_URL:0:20}..." # First 20 chars
echo "NODE_ENV: $NODE_ENV"
- File paths are different:
# Use absolute paths or $WORK_DIR
cd "$WORK_DIR" || exit 1 # Set by Flux-Orbit
npx prisma migrate deploy
- Dependencies not installed:
# Ensure this is in pre-deploy.sh (after dependencies)
# Dependencies are already installed before pre-deploy runs
npx prisma migrate deploy # Should work
Viewing Hook Logs
# SSH into container or use docker exec
docker exec YOUR_CONTAINER cat /app/logs/hooks.log
# Or view all logs
docker logs YOUR_CONTAINER
# Look for:
# - "Running hook: pre-deploy.sh"
# - "Hook completed successfully"
# - "Hook failed with exit code X"
Security Best Practices
1. Never Hardcode Secrets
❌ Bad:
#!/bin/bash
curl -X POST https://api.example.com \
-H "Authorization: Bearer sk-1234567890abcdef"
✅ Good:
#!/bin/bash
if [ -z "$API_TOKEN" ]; then
echo "ERROR: API_TOKEN not set"
exit 1
fi
curl -X POST https://api.example.com \
-H "Authorization: Bearer $API_TOKEN"
2. Validate Environment Variables
#!/bin/bash
set -e
# Validate required variables
required_vars=("DATABASE_URL" "REDIS_URL" "API_KEY")
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
echo "ERROR: $var is not set"
exit 1
fi
done
# Proceed with migrations
npx prisma migrate deploy
3. Use Secure Connections
#!/bin/bash
set -e
# Ensure DATABASE_URL uses SSL
if [[ "$DATABASE_URL" != *"sslmode=require"* ]]; then
export DATABASE_URL="${DATABASE_URL}?sslmode=require"
fi
python manage.py migrate
4. Limit Hook Permissions
#!/bin/bash
set -e
# Don't use sudo or run as root
# Flux-Orbit runs as non-root user 'appuser'
# This is fine:
npx prisma migrate deploy
# This would fail (no sudo):
# sudo systemctl restart nginx
Migration Guide
From Heroku Release Phase
Heroku (Procfile):
release: npx prisma migrate deploy
web: npm start
Flux-Orbit (pre-deploy.sh):
#!/bin/bash
set -e
npx prisma migrate deploy
From Render Deploy Hooks
Render (render.yaml):
services:
- type: web
buildCommand: npm run build
preDeployCommand: npx prisma migrate deploy
startCommand: npm start
Flux-Orbit (pre-deploy.sh):
#!/bin/bash
set -e
npx prisma migrate deploy
From Railway Deploy Hooks
Railway (railway.json):
{
"build": {
"buildCommand": "npm run build"
},
"deploy": {
"deployCommand": "npx prisma migrate deploy && npm start"
}
}
Flux-Orbit (pre-deploy.sh + package.json):
#!/bin/bash
# pre-deploy.sh
set -e
npx prisma migrate deploy
{
"scripts": {
"start": "node index.js"
}
}
Advanced Examples
Conditional Migrations
#!/bin/bash
# pre-deploy.sh
set -e
# Only run migrations in production
if [ "$NODE_ENV" = "production" ]; then
echo "Running production migrations..."
npx prisma migrate deploy
else
echo "Skipping migrations (not production)"
fi
Multi-Database Migrations
#!/bin/bash
# pre-deploy.sh
set -e
echo "Running migrations for primary database..."
DATABASE_URL="$PRIMARY_DATABASE_URL" npx prisma migrate deploy
echo "Running migrations for analytics database..."
DATABASE_URL="$ANALYTICS_DATABASE_URL" npx prisma migrate deploy
echo "All migrations completed"
Blue-Green Deployment Coordination
#!/bin/bash
# pre-deploy.sh
set -e
# Run migrations only on first peer in blue-green deployment
if [ "$FLUX_PEER_INDEX" = "0" ]; then
echo "Running migrations (peer 0)..."
npx prisma migrate deploy
else
echo "Skipping migrations (peer $FLUX_PEER_INDEX will use peer 0's migration)"
fi
Rollback on Migration Failure
#!/bin/bash
# pre-deploy.sh
set -e
CURRENT_COMMIT=$(git rev-parse HEAD)
echo "Current commit: $CURRENT_COMMIT"
echo "Running migrations..."
if npx prisma migrate deploy; then
echo "Migrations successful"
else
echo "ERROR: Migrations failed"
echo "Deployment will rollback automatically"
exit 1
fi
Next Steps
- Learn about CI/CD Integration
- Explore Environment Variables
- See Common Issues