Skip to content

Instantly share code, notes, and snippets.

@unnawut
Last active June 6, 2025 07:46
Show Gist options
  • Select an option

  • Save unnawut/d196de9cbd3ad2ec599f4dfae156177b to your computer and use it in GitHub Desktop.

Select an option

Save unnawut/d196de9cbd3ad2ec599f4dfae156177b to your computer and use it in GitHub Desktop.
Periodically check and update charon's CHARON_P2P_EXTERNAL_HOSTNAME for docker compose
#!/bin/bash
#
# Configurations
#
CHARON_PATH=~/charon-distributed-validator-node/
INTERVAL_MINUTES=15
SCRIPT_NAME="charon-ip"
LOG_LEVEL="${LOG_LEVEL:-info}" # Default log level, can be overridden by environment variable
#
# Logging functions
#
log_message() {
local level="$1"
local message="$2"
local timestamp="$(date)"
# Map log levels to systemd priorities
local priority
case "$level" in
"debug") priority="<7>" ;; # LOG_DEBUG
"info") priority="<6>" ;; # LOG_INFO
"warn") priority="<4>" ;; # LOG_WARNING
"err") priority="<3>" ;; # LOG_ERR
*) priority="<6>" ;; # Default to INFO
esac
# Format message with timestamp for stdout
local formatted_message="$timestamp [$level] $message"
# Output to stdout
echo "$formatted_message"
# Output to systemd journal using logger
# The -t flag sets the tag, -p sets priority
echo "$formatted_message" | logger -t "$SCRIPT_NAME" -p "user.$level"
# Alternative: Direct systemd journal logging (uncomment if preferred)
# systemd-cat -t "$SCRIPT_NAME" -p "$level" <<< "$message"
}
# Convenience functions for different log levels
log_debug() {
[[ "$LOG_LEVEL" == "debug" ]] && log_message "debug" "$1"
}
log_info() {
log_message "info" "$1"
}
log_warn() {
log_message "warn" "$1"
}
log_err() {
log_message "err" "$1"
}
#
# Do not edit below this line
#
DOTENV_PATH="$CHARON_PATH/.env"
log_info "Starting Charon IP monitor (interval: ${INTERVAL_MINUTES} minutes, log level: $LOG_LEVEL)"
while true; do
log_debug "Starting IP check cycle"
# Check .env file exists
if ! [ -f "$DOTENV_PATH" ]; then
log_err ".env file does not exist at $CHARON_PATH"
break
fi
log_debug "Found .env file at $DOTENV_PATH"
# Retrieve the config and actual IP values
CONFIG_IP=$(sed -n 's/^CHARON_P2P_EXTERNAL_HOSTNAME=//p' "$DOTENV_PATH")
log_debug "Querying public IP address"
PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
# Validate IP address format
if [[ ! "$PUBLIC_IP" =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$ ]]; then
log_err "Invalid IP address from dig: '$PUBLIC_IP'. Skipping this check cycle."
sleep $(($INTERVAL_MINUTES*60))
continue
fi
# Log the config and actual IP for manual inspection
log_info "CHARON_P2P_EXTERNAL_HOSTNAME=$CONFIG_IP Actual=$PUBLIC_IP"
# Update .env and reload charon service when an IP change is detected
if [[ "$CONFIG_IP" != "$PUBLIC_IP" ]]; then
log_warn "Public IP changed from $CONFIG_IP to $PUBLIC_IP. Reconfiguring Charon's .env file..."
# Update the .env file
if sed -i "s/^\(CHARON_P2P_EXTERNAL_HOSTNAME=\).*/\1$PUBLIC_IP/" "$DOTENV_PATH"; then
log_info "Successfully updated .env file with new IP: $PUBLIC_IP"
else
log_err "Failed to update .env file"
sleep $(($INTERVAL_MINUTES*60))
continue
fi
# Restart charon service
log_info "Restarting Charon service..."
if (cd "$CHARON_PATH" && docker compose down charon && docker compose up -d); then
log_info "Successfully restarted Charon service"
else
log_err "Failed to restart Charon service"
fi
else
log_debug "IP address unchanged, no action needed"
fi
log_debug "Sleeping for $INTERVAL_MINUTES minutes"
sleep $(($INTERVAL_MINUTES*60))
done
log_info "Charon IP monitor stopped"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment