Files
home/Projects/kompose/docs/content/3.guide/hooks.md
2025-10-11 10:24:25 +02:00

447 lines
12 KiB
Markdown
Executable File

---
title: Hooks System
description: Extend Kompose with custom hooks
---
Extend Kompose functionality with custom hooks for each stack. Hooks allow you to run custom logic before and after Docker Compose commands and database operations.
## Hook Types
### Database Hooks
| Hook | Timing | Arguments | Use Case |
|------|--------|-----------|----------|
| `hook_pre_db_export` | Before DB export | None | Prepare data, export schemas |
| `hook_post_db_export` | After DB export | `$1` = dump file path | Cleanup, notifications |
| `hook_pre_db_import` | Before DB import | `$1` = dump file path | Prepare environment, schema setup |
| `hook_post_db_import` | After DB import | `$1` = dump file path | Restart services, clear caches |
### Docker Compose Command Hooks
All Docker Compose commands support pre and post hooks. The hooks receive the full command arguments.
| Hook | Timing | Arguments | Use Case |
|------|--------|-----------|----------|
| `hook_pre_up` | Before `docker compose up` | `$@` = command args | Pre-flight checks, setup |
| `hook_post_up` | After `docker compose up` | `$@` = command args | Health checks, initialization |
| `hook_pre_down` | Before `docker compose down` | `$@` = command args | Graceful shutdown, backups |
| `hook_post_down` | After `docker compose down` | `$@` = command args | Cleanup, notifications |
| `hook_pre_start` | Before `docker compose start` | `$@` = command args | Validate state |
| `hook_post_start` | After `docker compose start` | `$@` = command args | Verify services |
| `hook_pre_stop` | Before `docker compose stop` | `$@` = command args | Save state |
| `hook_post_stop` | After `docker compose stop` | `$@` = command args | Cleanup |
| `hook_pre_restart` | Before `docker compose restart` | `$@` = command args | Prepare for restart |
| `hook_post_restart` | After `docker compose restart` | `$@` = command args | Verify restart |
| `hook_pre_build` | Before `docker compose build` | `$@` = command args | Code generation |
| `hook_post_build` | After `docker compose build` | `$@` = command args | Tag images, push to registry |
| `hook_pre_pull` | Before `docker compose pull` | `$@` = command args | Check registry availability |
| `hook_post_pull` | After `docker compose pull` | `$@` = command args | Verify images |
| `hook_pre_logs` | Before `docker compose logs` | `$@` = command args | Setup log filters |
| `hook_post_logs` | After `docker compose logs` | `$@` = command args | Process logs |
**Also supported:** `ps`, `exec`, `run`, `create`, `kill`, `pause`, `unpause`, `port`, `top`
::alert{type="info"}
**Note:** Post-command hooks only execute if the Docker Compose command succeeds (exit code 0).
::
## Creating Hooks
Create `<stack>/hooks.sh` in your stack directory:
```bash
#!/usr/bin/env bash
# Hook naming convention:
# - hook_pre_<command> - runs before the command
# - hook_post_<command> - runs after the command (only on success)
# Example: Setup before starting containers
hook_pre_up() {
local -a args=("$@")
echo " Running pre-flight checks..."
# Check if required directories exist
if [[ ! -d "./uploads" ]]; then
echo " Creating uploads directory..."
mkdir -p ./uploads
fi
return 0 # 0 = success, 1 = failure
}
# Example: Verify services after startup
hook_post_up() {
local -a args=("$@")
echo " Waiting for services to be healthy..."
# Wait for health check
sleep 5
if docker compose ps | grep -q "healthy"; then
echo " ✓ Services are healthy"
return 0
else
echo " ⚠ Warning: Some services may not be healthy"
return 0 # Non-critical, don't fail
fi
}
# Example: Backup before shutdown
hook_pre_down() {
echo " Creating backup before shutdown..."
./backup.sh
return 0
}
# Example: Restart dependent services
hook_post_restart() {
echo " Restarting dependent services..."
# Restart a related service that depends on this stack
return 0
}
```
## Real-World Examples
### Example 1: Directus Schema Management (sexy stack)
The `sexy` stack uses hooks for Directus schema synchronization:
```bash
#!/usr/bin/env bash
# Export Directus schema before database export
hook_pre_db_export() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local snapshot_file="directus_schema_${timestamp}.yaml"
echo " Exporting Directus schema snapshot..."
if docker exec sexy_api npx directus schema snapshot "$snapshot_file" > /dev/null 2>&1; then
echo " Schema snapshot saved: $snapshot_file"
return 0
else
echo " Warning: Could not export schema snapshot"
return 0 # Don't fail the entire export
fi
}
# Import Directus schema before database import
hook_pre_db_import() {
local dump_file="$1"
# Find most recent schema snapshot
local snapshot_file=$(ls -t directus_schema_*.yaml 2>/dev/null | head -1)
if [[ -z "$snapshot_file" ]]; then
echo " No schema snapshot found, skipping"
return 0
fi
echo " Applying Directus schema from: $snapshot_file"
if docker exec sexy_api npx directus schema apply "$snapshot_file" > /dev/null 2>&1; then
echo " Schema applied successfully"
return 0
else
echo " Warning: Could not apply schema snapshot"
return 0
fi
}
# Reload Directus after import
hook_post_db_import() {
echo " Restarting Directus to apply changes..."
docker restart sexy_api > /dev/null 2>&1
return 0
}
```
### Example 2: Health Monitoring
```bash
#!/usr/bin/env bash
# Send notification when services start
hook_post_up() {
local -a args=("$@")
echo " Notifying monitoring system..."
# Example: Send webhook
curl -X POST https://monitoring.example.com/webhook \
-H "Content-Type: application/json" \
-d "{\"stack\": \"${stack}\", \"status\": \"up\"}" \
2>/dev/null
return 0
}
# Alert on shutdown
hook_post_down() {
echo " Sending shutdown notification..."
curl -X POST https://monitoring.example.com/webhook \
-H "Content-Type: application/json" \
-d "{\"stack\": \"${stack}\", \"status\": \"down\"}" \
2>/dev/null
return 0
}
```
### Example 3: Automated Backups
```bash
#!/usr/bin/env bash
# Backup volumes before rebuild
hook_pre_build() {
echo " Backing up volumes before rebuild..."
local backup_dir="./backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"
# Backup volumes
docker run --rm \
-v app_data:/data \
-v "$backup_dir:/backup" \
alpine tar czf /backup/data.tar.gz -C /data .
echo " Backup saved to: $backup_dir"
return 0
}
# Tag and push after successful build
hook_post_build() {
echo " Tagging and pushing image..."
local image="${COMPOSE_PROJECT_NAME}_app"
local tag="$(date +%Y%m%d-%H%M%S)"
docker tag "$image:latest" "$image:$tag"
docker push "$image:$tag"
return 0
}
```
### Example 4: Cache Warming
```bash
#!/usr/bin/env bash
# Warm caches after starting
hook_post_up() {
echo " Warming application caches..."
# Wait for app to be ready
sleep 10
# Hit cache endpoints
curl -s http://localhost:3000/api/cache/warm > /dev/null
return 0
}
# Clear caches before restart
hook_pre_restart() {
echo " Clearing application caches..."
docker exec app_container redis-cli FLUSHALL
return 0
}
```
## Hook Execution Flow
### Database Export Flow
```
1. hook_pre_db_export
2. pg_dump (database export)
3. hook_post_db_export (receives dump file path)
```
### Database Import Flow
```
1. hook_pre_db_import (receives dump file path)
2. Database drop & recreate
3. psql (database import)
4. hook_post_db_import (receives dump file path)
```
### Docker Compose Command Flow
```
1. hook_pre_<command> (receives command args)
2. docker compose <command>
3. hook_post_<command> (only if command succeeds, receives command args)
```
## Testing Hooks
```bash
# Preview hook execution with dry-run
./kompose.sh sexy db:export --dry-run
./kompose.sh sexy up -d --dry-run
# Execute with hooks
./kompose.sh sexy db:export
./kompose.sh sexy up -d
# Test specific commands with hooks
./kompose.sh news restart
./kompose.sh blog build
./kompose.sh "*" down
```
## Accessing Command Arguments
Hooks receive the full command and its arguments:
```bash
hook_pre_up() {
local -a args=("$@")
# Access specific arguments
echo " Command: ${args[0]}" # "up"
echo " All args: ${args[*]}" # "up -d --build"
# Check for specific flags
if [[ " ${args[*]} " =~ " --build " ]]; then
echo " Build flag detected, running pre-build tasks..."
fi
return 0
}
```
## Hook Best Practices
### ✅ DO:
- **Return proper exit codes**: `0` for success, `1` for failure
- **Use indented output**: `echo " Message"` for better readability
- **Make non-critical operations non-blocking**: Return `0` even on minor failures
- **Check container status** before using `docker exec`
- **Test in dry-run mode** first: `./kompose.sh stack command --dry-run`
- **Use environment variables** available from `.env` files
- **Add timeouts** for long-running operations
- **Document your hooks** with comments
### ❌ DON'T:
- **Assume containers are running** before executing commands
- **Use blocking operations** without timeouts
- **Forget error handling** for external commands
- **Hardcode paths, credentials, or hostnames**
- **Perform destructive operations** in `pre_*` hooks without confirmation
- **Make hooks too complex** - consider separate scripts if needed
- **Ignore exit codes** from important operations
## Available Environment Variables
Hooks have access to all environment variables from:
- Root `.env` file
- Stack-specific `.env` file
- CLI overrides (`-e KEY=VALUE`)
Common variables:
```bash
$SCRIPT_DIR # Root kompose directory
$stack # Current stack name
$COMPOSE_PROJECT_NAME
$DB_NAME
$DB_HOST
$DB_PORT
$DB_USER
$DB_PASSWORD
# ... all variables from .env files
```
## Troubleshooting
### Hook not executing
1. Verify `hooks.sh` has execute permissions: `chmod +x <stack>/hooks.sh`
2. Check function naming: `hook_pre_<command>` or `hook_post_<command>`
3. Test with dry-run mode to see if hooks are detected
4. Check for bash syntax errors: `bash -n <stack>/hooks.sh`
### Hook failing
1. Add `set -x` at the top of your hook for debugging
2. Check return codes: `return 0` for success
3. Verify container names and states
4. Check environment variables are loaded
5. Look at Kompose output for error messages
### Best debugging approach
```bash
# Enable dry-run to see what would execute
./kompose.sh stack command --dry-run
# Add debug output in your hook
hook_pre_up() {
echo " [DEBUG] Stack: $stack"
echo " [DEBUG] Args: $*"
echo " [DEBUG] Container: $(docker ps --filter name=$stack)"
# ... your hook logic
return 0
}
```
## Advanced Patterns
### Conditional Execution
```bash
hook_pre_up() {
# Only run in production
if [[ "${ENVIRONMENT}" == "production" ]]; then
echo " Running production pre-flight checks..."
./production-checks.sh
fi
return 0
}
```
### Parallel Hook Execution
```bash
hook_post_up() {
echo " Running parallel initialization tasks..."
# Background jobs
(cache_warm) &
(index_rebuild) &
# Wait for all background jobs
wait
return 0
}
```
### Hook Chaining
```bash
hook_post_restart() {
# Call another stack's command
echo " Restarting dependent services..."
"${SCRIPT_DIR}/kompose.sh" dependent-stack restart
return 0
}
```
## Security Considerations
- Never log sensitive information (passwords, tokens)
- Validate user input in hooks that accept parameters
- Use quotes around variables to prevent injection
- Restrict file permissions on `hooks.sh`: `chmod 750 hooks.sh`
- Avoid executing user-supplied data without validation
- Use absolute paths or `$SCRIPT_DIR` for file references
## Related
- [Database Operations](/guide/database) - Learn about database import/export
- [Stack Management](/guide/stack-management) - Managing multiple stacks
- [Configuration](/guide/configuration) - Environment variables and settings