Deployment Hooks

Deployment hooks are functionality for executing custom commands before and after deployment. They enable flexible customization of the deployment process including database backups, service management, and notification sending.

Overview

Deployment hooks are powerful functionality for customizing Dewy's automated deployment process. They can execute arbitrary shell commands before and after application deployment, enabling various use cases such as database operations, external service integration, and validation processing.

Key Features:

  • Flexible execution timing: Execution control before and after deployment
  • Complete environment access: Full access to environment variables and file system
  • Detailed execution results: Recording of stdout/stderr, exit codes, and execution time
  • Notification integration: Sending execution results to configured notification channels

Hook Types and Behavior

Before Deploy Hook

Hook executed before deployment starts.

Execution Timing:

  • After artifact download
  • Before file extraction and symbolic link creation
  • Before application restart

Important Behavior:

# If Before Hook fails, deployment is aborted
dewy server --registry ghr://owner/repo \
  --before-deploy-hook "scripts/pre-check.sh" \
  -- /opt/myapp/current/myapp
warningDeployment Abort Condition

If Before Deploy Hook exits with a non-zero exit code, the entire deployment process is aborted. This behavior enables safe deployment prevention when preconditions are not met.

Nothing is extracted when the hook fails, and the current symlink keeps pointing at the release that is running. Dewy records the run as failed, sends the error notification, and retries the same version on the next poll, so a deploy stopped by a transient cause goes through once that cause is gone. Consecutive failures engage the polling backoff, which widens the interval between retries.

After Deploy Hook

Hook executed after deployment succeeds.

Execution Timing:

  • After file extraction and symbolic link creation completion
  • Before the application is started or restarted (for server command)
  • After the rolling deployment completes (for container command)

Important Behavior:

# Deployment is treated as successful even if After Hook fails
dewy server --registry ghr://owner/repo \
  --after-deploy-hook "scripts/post-deploy-validation.sh" \
  -- /opt/myapp/current/myapp
note

After Deploy Hook failure does not affect deployment success status. However, errors are logged and notifications are sent if configured.

Rollback Behavior

Hooks are not executed when a rollback restores the previous release.

With the server command and --health-path, Dewy verifies the new release after restarting the application. When that health check fails, Dewy records the version as blocked, restores the previous release directory, repoints the current symlink, and brings the server up again. Neither the Before Deploy Hook nor the After Deploy Hook runs during those steps.

Order of operations for a server deployment:

  1. Before Deploy Hook
  2. Artifact extraction and current symlink swap
  3. After Deploy Hook
  4. Application start or restart
  5. Health check (only with --health-path)
  6. Rollback when step 5 fails

The After Deploy Hook runs at step 3, before the application is restarted and before the health check. It has therefore already run against the new release by the time step 6 restores the previous one. After a rollback:

  • The After Deploy Hook has run for a release that is no longer live.
  • No hook has run for the restored release that is now live.
  • A hook that derives state from the release path, such as a symlink of its own, a cache warm-up, or a rendered configuration file, still points at the failed release.

The notifier is updated to the restored release path, so notification content reflects the release that is actually running.

warningHooks and rollback

Write the After Deploy Hook so that the state it produces is either independent of a specific release directory, or safe to correct manually after a rollback notification. Whether a rollback should run a dedicated hook is under discussion in issue #493.

With the container command the situation does not arise. A failed rolling deployment is rolled back inside the deployment step, before the After Deploy Hook runs, so no hook has run against the reverted release.

Execution Environment and Constraints

Execution Environment

Hooks are executed in the following environment:

Shell Execution:

/bin/sh -c "your-command"

Working Directory:

  • Dewy's execution directory (usually application root directory)

Environment Variables:

  • Inherits all environment variables from Dewy process
  • Full access to runtime environment variables

Execution Result Capture

The following information is recorded during hook execution:

ItemDescriptionUsage
CommandExecuted command stringDebugging and logging
StdoutStandard output contentChecking execution results
StderrStandard error outputUnderstanding error content
ExitCodeProcess exit codeSuccess/failure determination
DurationExecution timePerformance monitoring

Log Output Example:

{
  "time": "2024-03-15T10:30:45Z",
  "level": "INFO",
  "msg": "Execute hook success",
  "command": "backup-database.sh",
  "stdout": "Backup completed successfully",
  "stderr": "",
  "exit_code": 0,
  "duration": "2.5s"
}

Configuration Methods

Command Line Configuration

# Basic format
dewy server --registry <registry-url> \
  --before-deploy-hook "<command>" \
  --after-deploy-hook "<command>" \
  -- <application-command>

Configuration Examples

Simple command execution:

dewy server --registry ghr://owner/repo \
  --before-deploy-hook "echo 'Starting deployment'" \
  --after-deploy-hook "echo 'Deployment completed'" \
  -- /opt/myapp/current/myapp

Multiple command coordination:

dewy server --registry ghr://owner/repo \
  --before-deploy-hook "systemctl stop nginx && backup-db.sh" \
  --after-deploy-hook "systemctl start nginx && send-notification.sh" \
  -- /opt/myapp/current/myapp

Script file execution:

dewy server --registry ghr://owner/repo \
  --before-deploy-hook "/opt/scripts/pre-deploy.sh" \
  --after-deploy-hook "/opt/scripts/post-deploy.sh" \
  -- /opt/myapp/current/myapp

Practical Use Cases

Database Operations

Automatic backup execution:

# PostgreSQL backup
--before-deploy-hook "pg_dump myapp_db > /backup/myapp_$(date +%Y%m%d_%H%M%S).sql"

# MySQL backup
--before-deploy-hook "mysqldump -u root -p myapp_db > /backup/myapp_$(date +%Y%m%d_%H%M%S).sql"

Migration execution:

# Rails migration
--after-deploy-hook "cd /opt/myapp/current && bundle exec rake db:migrate"

# Django migration
--after-deploy-hook "cd /opt/myapp/current && python manage.py migrate"

# Go migration (using migrate tool)
--after-deploy-hook "migrate -path /opt/myapp/current/migrations -database 'postgres://...' up"

Service Management

Related service control:

# Nginx temporary stop and restart
--before-deploy-hook "systemctl stop nginx"
--after-deploy-hook "systemctl start nginx && systemctl reload nginx"

# Load balancer disconnection
--before-deploy-hook "curl -X DELETE http://lb:8080/servers/$(hostname)"
--after-deploy-hook "curl -X POST http://lb:8080/servers/$(hostname)"

Health check execution:

# Application startup confirmation
--after-deploy-hook "timeout 30 bash -c 'until curl -f http://localhost:8080/health; do sleep 1; done'"

# Database connection confirmation
--before-deploy-hook "pg_isready -h localhost -p 5432 -d myapp_db"

Notification and Monitoring

External system notifications:

# Send deployment event to Datadog
--after-deploy-hook "curl -X POST https://api.datadoghq.com/api/v1/events \
  -H 'DD-API-KEY: ${DD_API_KEY}' \
  -d '{\"title\":\"Deployment\",\"text\":\"App deployed\"}'"

# PagerDuty notification
--after-deploy-hook "scripts/notify-pagerduty.sh deployment-success"

Metrics collection:

# Record deployment time
--before-deploy-hook "echo $(date +%s) > /tmp/deploy_start"
--after-deploy-hook "echo 'Deploy time: '$(($(date +%s) - $(cat /tmp/deploy_start)))'s'"

Error Handling and Recovery

Before Hook Failure

Behavior when Before Hook fails:

  1. Automatic deployment abort: Entire process stops
  2. Error log recording: Detailed execution results output to logs
  3. Notification sending: Error notifications sent if configured
  4. Current state maintenance: Existing application is unaffected

Recommended recovery procedure:

# 1. Check error cause
tail -f /var/log/dewy.log

# 2. Manual hook execution test
/bin/sh -c "your-before-hook-command"

# 3. Retry deployment after problem fix
# Dewy automatically retries on next polling

After Hook Failure

Behavior when After Hook fails:

  1. Deployment processed as successful: Application runs with new version
  2. Error log recording: Detailed failure content logged
  3. Notification sending: Warning notifications sent if configured
  4. Manual response recommended: Administrator confirmation and response needed

Security Considerations

Permission Management

Execution user permission settings:

# Run Dewy with dedicated user
sudo useradd -r -s /bin/bash dewy
sudo chown -R dewy:dewy /opt/myapp

# Service definition with minimum required permissions
# /etc/systemd/system/dewy.service
[Service]
User=dewy
Group=dewy

Sudo usage considerations:

# ❌ Dangerous: Password prompt hangs
--before-deploy-hook "sudo systemctl stop nginx"

# ✅ Safe: NOPASSWD or dedicated user permission settings
--before-deploy-hook "systemctl stop nginx"  # systemd user session

Command Injection Prevention

Safe command writing:

# ✅ Safe: Protection with quotes
--before-deploy-hook "backup-db.sh --name 'myapp_backup'"

# ❌ Dangerous: Direct environment variable expansion
--before-deploy-hook "echo $USER_INPUT"

# ✅ Safe: Proper environment variable usage
--before-deploy-hook "scripts/safe-command.sh"  # Handle properly in script

Best Practices

Production Environment Recommended Settings

Safe backup strategy:

dewy server --registry ghr://company/myapp \
  --before-deploy-hook "scripts/production-backup.sh" \
  --after-deploy-hook "scripts/production-validation.sh" \
  --notifier "slack://ops-alerts" \
  -- /opt/myapp/current/myapp

Example production-backup.sh:

#!/bin/bash
set -euo pipefail

# Database backup
pg_dump myapp_production > "/backup/pre-deploy-$(date +%Y%m%d_%H%M%S).sql"

# Configuration file backup
cp /opt/myapp/current/config.yml "/backup/config-$(date +%Y%m%d_%H%M%S).yml"

# Health check
curl -f http://localhost:8080/health || exit 1

echo "Pre-deployment backup completed successfully"

Troubleshooting

Common Issues

Permission errors:

# Problem: Permission denied
--before-deploy-hook "systemctl stop nginx"

# Solution: Check and adjust user permissions
sudo usermod -a -G sudo dewy
# or
sudo visudo  # Configure NOPASSWD

Path issues:

# Problem: command not found
--after-deploy-hook "npm install"

# Solution: Full path or PATH configuration
--after-deploy-hook "/usr/local/bin/npm install"
# or
--after-deploy-hook "PATH=/usr/local/bin:$PATH npm install"

Debugging Methods

Gradual problem isolation:

# 1. Start with simple commands
--before-deploy-hook "echo 'Hook test'"

# 2. Gradually increase complexity
--before-deploy-hook "echo 'Hook test' && date"

# 3. Actual command
--before-deploy-hook "your-actual-command"

Manual execution verification:

# Test in same environment as Dewy
cd /opt/myapp
sudo -u dewy /bin/sh -c "your-hook-command"
  • Architecture - Position of hooks in overall deployment process
  • Notifier - Detailed notification channel configuration (Slack/Mail)
  • Versioning - Version detection as deployment trigger
  • FAQ - Frequently asked questions about deployment hooks