Skip to content

Instantly share code, notes, and snippets.

@Oddly
Created May 22, 2025 20:19
Show Gist options
  • Save Oddly/75b46739fc1ef6d20ead78857dec6506 to your computer and use it in GitHub Desktop.
Save Oddly/75b46739fc1ef6d20ead78857dec6506 to your computer and use it in GitHub Desktop.
Caddy update script (with cloudflare plugin) in bash
#!/bin/bash
# Set log file
LOG_FILE="/tmp/caddy_update_$(date +%Y%m%d_%H%M%S).log"
# Default values
FORCE_UPDATE=false
SPECIFIC_VERSION=""
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force)
FORCE_UPDATE=true
shift
;;
-v|--version)
SPECIFIC_VERSION="$2"
shift 2
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [-f|--force] [-v|--version VERSION]"
echo " -f, --force Force update even if already on latest version"
echo " -v, --version Install specific version (e.g., v2.9.0)"
exit 1
;;
esac
done
# Function to log messages
log() {
echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" | tee -a "$LOG_FILE"
}
{
log "Starting Caddy update process with Cloudflare DNS plugin"
# Check if Caddy is installed
if ! command -v caddy &> /dev/null; then
log "ERROR: Caddy is not installed or not in PATH"
exit 1
fi
# Get current version
CURRENT_VERSION=$(caddy version | head -n 1)
CURRENT_VERSION_TAG=$(echo "$CURRENT_VERSION" | cut -d' ' -f1)
log "Current Caddy version: $CURRENT_VERSION"
# Determine target version
if [ -n "$SPECIFIC_VERSION" ]; then
# If specific version is provided, use it
TARGET_VERSION="$SPECIFIC_VERSION"
log "Target version specified: $TARGET_VERSION"
# Validate version format
if [[ ! "$TARGET_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
log "ERROR: Invalid version format. Expected format: vX.Y.Z (e.g., v2.9.0)"
exit 1
fi
else
# Get latest Caddy version
log "Checking for latest Caddy version"
TARGET_VERSION=$(curl -s https://api.github.com/repos/caddyserver/caddy/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)
if [ -z "$TARGET_VERSION" ]; then
log "ERROR: Failed to get latest version tag"
exit 1
fi
log "Latest version tag: $TARGET_VERSION"
fi
# Check if Cloudflare plugin is already included
CLOUDFLARE_INCLUDED=false
if caddy list-modules 2>/dev/null | grep -q "dns.providers.cloudflare"; then
CLOUDFLARE_INCLUDED=true
log "Cloudflare DNS plugin is already included in the current binary"
else
log "Cloudflare DNS plugin is not included in the current binary"
fi
# Skip update if already on target version and has Cloudflare plugin, unless forced
if [ "$CURRENT_VERSION_TAG" = "$TARGET_VERSION" ] && [ "$CLOUDFLARE_INCLUDED" = true ] && [ "$FORCE_UPDATE" = false ]; then
log "Already running the target version ($TARGET_VERSION) with Cloudflare DNS plugin"
log "No update needed (use -f to force update)"
exit 0
fi
# Check if Go is installed (required for xcaddy)
if ! command -v go &> /dev/null; then
log "Installing Go (required for xcaddy)"
if ! apt-get update && apt-get install -y golang; then
log "ERROR: Failed to install Go"
exit 1
fi
fi
# Find Go path
GOPATH=$(go env GOPATH)
if [ -z "$GOPATH" ]; then
GOPATH="$HOME/go"
fi
log "Using GOPATH: $GOPATH"
# Add Go bin to PATH
export PATH="$GOPATH/bin:$PATH"
log "Updated PATH: $PATH"
# Install or update xcaddy
log "Installing/updating xcaddy"
if ! go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest; then
log "ERROR: Failed to install xcaddy"
exit 1
fi
# Verify xcaddy is installed
if ! command -v xcaddy &> /dev/null; then
log "ERROR: xcaddy not found in PATH after installation"
log "Looking for xcaddy in GOPATH/bin: $GOPATH/bin"
if [ -f "$GOPATH/bin/xcaddy" ]; then
log "Found xcaddy at $GOPATH/bin/xcaddy"
XCADDY="$GOPATH/bin/xcaddy"
else
log "ERROR: Cannot find xcaddy executable"
exit 1
fi
else
XCADDY=$(which xcaddy)
log "Found xcaddy at $XCADDY"
fi
# Create a temporary build directory
BUILD_DIR=$(mktemp -d)
log "Building Caddy with Cloudflare DNS plugin in $BUILD_DIR"
# Build Caddy with Cloudflare DNS plugin
cd "$BUILD_DIR"
log "Running xcaddy build with Cloudflare DNS plugin for version $TARGET_VERSION"
if ! $XCADDY build "$TARGET_VERSION" --with github.com/caddy-dns/cloudflare; then
log "ERROR: Failed to build Caddy with Cloudflare DNS plugin"
rm -rf "$BUILD_DIR"
exit 1
fi
# Create a backup of the current binary
CADDY_PATH=$(which caddy)
BACKUP_PATH="${CADDY_PATH}.bak.$(date +%Y%m%d%H%M%S)"
log "Creating backup of current Caddy binary at $BACKUP_PATH"
if ! cp "$CADDY_PATH" "$BACKUP_PATH"; then
log "ERROR: Failed to create backup of current Caddy binary"
rm -rf "$BUILD_DIR"
exit 1
fi
# Replace the binary
log "Replacing Caddy binary"
if ! mv "$BUILD_DIR/caddy" "$CADDY_PATH"; then
log "ERROR: Failed to replace Caddy binary"
log "Restoring from backup"
mv "$BACKUP_PATH" "$CADDY_PATH"
rm -rf "$BUILD_DIR"
exit 1
fi
# Set proper permissions
log "Setting proper permissions"
chmod 755 "$CADDY_PATH"
# Verify the new version
NEW_VERSION=$(caddy version | head -n 1)
log "New Caddy version: $NEW_VERSION"
# Verify Cloudflare plugin is included
log "Verifying Cloudflare DNS plugin is included"
if ! caddy list-modules | grep -q "dns.providers.cloudflare"; then
log "ERROR: Cloudflare DNS plugin not found in the built binary"
log "Restoring from backup"
mv "$BACKUP_PATH" "$CADDY_PATH"
rm -rf "$BUILD_DIR"
exit 1
else
log "Cloudflare DNS plugin successfully included"
fi
# Restart Caddy service
log "Restarting Caddy service"
if systemctl is-active --quiet caddy; then
if ! systemctl restart caddy; then
log "ERROR: Failed to restart Caddy service"
log "Restoring from backup"
mv "$BACKUP_PATH" "$CADDY_PATH"
systemctl restart caddy
rm -rf "$BUILD_DIR"
exit 1
fi
else
log "WARNING: Caddy service is not running or not managed by systemd"
fi
# Clean up
log "Cleaning up temporary files"
rm -rf "$BUILD_DIR"
log "Caddy update with Cloudflare DNS plugin completed successfully"
log "Updated to version: $TARGET_VERSION"
log "Backup of previous version saved at: $BACKUP_PATH"
} 2>&1
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment