Skip to main content
This guide provides the official procedure for manually updating UTMStack using the automated update script. The script handles the complete update process including downloading the latest installer, managing services, and monitoring the update progress.

Overview

The UTMStack update script is an automated solution that:
  • Downloads the latest UTMStack installer
  • Manages service lifecycle during updates
  • Monitors the update process
  • Provides detailed logging and error handling
The script is designed to always download and install the latest available version of UTMStack from the official GitHub repository.

Prerequisites

Before proceeding with the update, ensure the following requirements are met:
Critical Requirements:
  • Root or sudo privileges
  • Active internet connection
  • systemd-based Linux distribution
  • The script must be executed in the same directory where the UTMStack installer is located
  • Minimum 2GB of available disk space
  • All UTMStack services are running properly
  • Recent backup of your data (recommended)

Update Procedure

Follow these steps carefully to perform the manual update:
1

Navigate to Installation Directory

First, locate and navigate to the directory containing your UTMStack installer.If you don’t know the installer location, use this command to find it:
find /root /home -name 'installer' -type f 2>/dev/null
Once located, navigate to that directory:
cd /path/to/installer/directory
Replace /path/to/installer/directory with the actual path found in the previous command.
2

Create the Update Script

Create a new file named update.sh in the installer directory:
nano update.sh
Copy the complete script below and paste it into the editor:
#!/bin/bash

# ============================================================================
# UTMStack Update Script
# ============================================================================
# Description:
#   Automated update script for UTMStack infrastructure. This script handles
#   the complete update process including downloading the latest installer,
#   managing services, and monitoring the update progress.
#
# Prerequisites:
#   - Must be executed in the directory containing the previous UTMStack installer
#   - Root/sudo privileges required
#   - Active internet connection
#   - systemd-based Linux distribution
#
# Usage:
#   bash update.sh [OPTIONS]
#
# Options:
#   -h, --help       Display this help message
#   -v, --verbose    Enable verbose output
#   -d, --dry-run    Perform a dry run without making changes
#
# Exit Codes:
#   0 - Success
#   1 - General error
#   2 - Missing prerequisites
#   3 - Download failure
#   4 - Service management failure
# ============================================================================

set -euo pipefail  # Exit on error, undefined variables, and pipe failures
IFS=$'\n\t'        # Set Internal Field Separator for better word splitting

# ============================================================================
# Global Variables
# ============================================================================
readonly SCRIPT_VERSION="1.0.0"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly INSTALLER_URL="https://github.com/utmstack/UTMStack/releases/latest/download/installer"
readonly INSTALLER_FILE="installer"
readonly LOG_FILE="/utmstack/updates/logs/utmstack-updater.log"
readonly LOG_DIR="/utmstack/updates/logs"
readonly SERVICE_NAME="UTMStackComponentsUpdater"
readonly TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# Script options
VERBOSE=false
DRY_RUN=false

# Color codes for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color

# ============================================================================
# Utility Functions
# ============================================================================

# Print formatted messages with timestamp
log_info() {
    echo -e "${BLUE}[INFO]${NC} [$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} [$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} [$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} [$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
}

log_verbose() {
    if [[ "${VERBOSE}" == true ]]; then
        echo -e "${BLUE}[VERBOSE]${NC} [$(date '+%Y-%m-%d %H:%M:%S')] $*"
    fi
}

# Display help message
show_help() {
    sed -n '/^# ===.*UTMStack Update Script/,/^# ===.*$/p' "$0" | \
    sed 's/^# \?//' | \
    sed "s/SCRIPT_VERSION/${SCRIPT_VERSION}/"
}

# Print section header
print_header() {
    local message="$1"
    local width=60
    echo ""
    echo "$(printf '=%.0s' $(seq 1 $width))"
    echo "  $message"
    echo "$(printf '=%.0s' $(seq 1 $width))"
    echo ""
}

# Print step information
print_step() {
    local current="$1"
    local total="$2"
    local description="$3"
    echo ""
    log_info "Step [$current/$total]: $description"
    echo "$(printf -- '-%.0s' $(seq 1 60))"
}

# ============================================================================
# Validation Functions
# ============================================================================

# Check if script is run with sufficient privileges
check_privileges() {
    log_verbose "Checking for sudo privileges..."
    if [[ $EUID -ne 0 ]] && ! sudo -n true 2>/dev/null; then
        log_error "This script requires sudo privileges"
        log_info "Please run with: sudo bash $0"
        return 2
    fi
    log_verbose "Privileges check passed"
    return 0
}

# Verify required commands are available
check_dependencies() {
    log_verbose "Checking required dependencies..."
    local missing_deps=()
    
    for cmd in wget chmod systemctl tail; do
        if ! command -v "$cmd" &> /dev/null; then
            missing_deps+=("$cmd")
        fi
    done
    
    if [[ ${#missing_deps[@]} -gt 0 ]]; then
        log_error "Missing required commands: ${missing_deps[*]}"
        return 2
    fi
    
    log_verbose "All dependencies are available"
    return 0
}

# Verify internet connectivity
check_connectivity() {
    log_verbose "Checking internet connectivity..."
    if ! wget --spider --quiet --timeout=10 "https://github.com" 2>/dev/null; then
        log_error "No internet connectivity detected"
        log_info "Please check your network connection and try again"
        return 3
    fi
    log_verbose "Internet connectivity verified"
    return 0
}

# Check if systemd service exists
check_service_exists() {
    log_verbose "Checking if ${SERVICE_NAME} service exists..."
    if ! systemctl list-unit-files | grep -q "^${SERVICE_NAME}.service"; then
        log_warning "Service ${SERVICE_NAME} not found in systemd"
        return 1
    fi
    log_verbose "Service exists"
    return 0
}

# Check if script is being executed from installer directory
check_installer_directory() {
    log_verbose "Checking if installer exists in current directory..."
    
    if [[ ! -f "${INSTALLER_FILE}" ]]; then
        echo ""
        echo "$(printf '=%.0s' $(seq 1 60))"
        log_error "INSTALLER NOT FOUND IN EXECUTION DIRECTORY"
        echo "$(printf '=%.0s' $(seq 1 60))"
        echo ""
        log_info "This script must be executed from the directory where the"
        log_info "UTMStack installer is located."
        echo ""
        log_info "To find your installer location, run:"
        log_info "  find /root /home -name 'installer' -type f 2>/dev/null"
        echo ""
        log_info "Then navigate to that directory and run the script:"
        log_info "  cd /path/to/installer/directory"
        log_info "  bash update.sh"
        echo ""
        return 2
    fi
    
    log_verbose "Installer file found: ${INSTALLER_FILE}"
    log_info "Installer found in current directory: $(pwd)"
    return 0
}

# ============================================================================
# Core Functions
# ============================================================================

# Remove old installer file
remove_old_installer() {
    print_step 1 8 "Cleaning up old installer"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would remove ${INSTALLER_FILE}"
        return 0
    fi
    
    if [[ -f "${INSTALLER_FILE}" ]]; then
        log_info "Removing existing installer file..."
        rm -f "${INSTALLER_FILE}"
        log_success "Old installer removed successfully"
    else
        log_info "No existing installer found (clean state)"
    fi
}

# Download the latest installer
download_installer() {
    print_step 2 8 "Downloading latest installer"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would download from ${INSTALLER_URL}"
        return 0
    fi
    
    log_info "Fetching installer from: ${INSTALLER_URL}"
    
    if wget --timeout=300 --tries=3 --progress=bar:force "${INSTALLER_URL}" -O "${INSTALLER_FILE}" 2>&1; then
        log_success "Installer downloaded successfully"
        
        # Verify the downloaded file
        if [[ ! -s "${INSTALLER_FILE}" ]]; then
            log_error "Downloaded file is empty"
            return 3
        fi
    else
        log_error "Failed to download installer"
        log_info "Please check your internet connection and try again"
        return 3
    fi
}

# Make installer executable
set_executable_permissions() {
    print_step 3 8 "Setting executable permissions"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would set executable permissions on ${INSTALLER_FILE}"
        return 0
    fi
    
    log_info "Making installer executable..."
    chmod +x "${INSTALLER_FILE}"
    log_success "Permissions set successfully"
    
    log_verbose "File permissions: $(ls -lh ${INSTALLER_FILE})"
}

# Stop the UTMStack service
stop_service() {
    print_step 4 8 "Stopping ${SERVICE_NAME} service"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would stop ${SERVICE_NAME} service"
        return 0
    fi
    
    if ! check_service_exists; then
        log_warning "Service not found, skipping stop operation"
        return 0
    fi
    
    log_info "Stopping service..."
    if sudo systemctl stop "${SERVICE_NAME}"; then
        log_success "Service stopped successfully"
        
        # Wait for service to fully stop
        local timeout=30
        local elapsed=0
        while systemctl is-active --quiet "${SERVICE_NAME}" && [[ $elapsed -lt $timeout ]]; do
            sleep 1
            ((elapsed++))
            log_verbose "Waiting for service to stop... (${elapsed}s)"
        done
        
        if systemctl is-active --quiet "${SERVICE_NAME}"; then
            log_warning "Service did not stop within ${timeout} seconds"
            return 4
        fi
    else
        log_warning "Failed to stop service (may not be running)"
    fi
}

# Clean up old log file
cleanup_log_file() {
    print_step 5 8 "Cleaning up old log file"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would remove ${LOG_FILE}"
        return 0
    fi
    
    # Ensure log directory exists
    if [[ ! -d "${LOG_DIR}" ]]; then
        log_info "Creating log directory: ${LOG_DIR}"
        sudo mkdir -p "${LOG_DIR}"
    fi
    
    if [[ -f "${LOG_FILE}" ]]; then
        # Backup old log before removing
        local backup_file="${LOG_FILE}.$(date +%Y%m%d_%H%M%S).bak"
        log_info "Backing up old log to: ${backup_file}"
        sudo cp "${LOG_FILE}" "${backup_file}"
        
        log_info "Removing old log file..."
        sudo rm -f "${LOG_FILE}"
        log_success "Log file cleaned up"
    else
        log_info "No existing log file found"
    fi
}

# Start the UTMStack service
start_service() {
    print_step 6 8 "Starting ${SERVICE_NAME} service"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would start ${SERVICE_NAME} service"
        return 0
    fi
    
    if ! check_service_exists; then
        log_error "Service not found, cannot start"
        return 4
    fi
    
    log_info "Starting service..."
    if sudo systemctl start "${SERVICE_NAME}"; then
        log_success "Service started successfully"
        
        # Verify service is running
        sleep 2
        if systemctl is-active --quiet "${SERVICE_NAME}"; then
            log_success "Service is running and active"
        else
            log_error "Service started but is not active"
            return 4
        fi
    else
        log_error "Failed to start service"
        log_info "Check service status with: sudo systemctl status ${SERVICE_NAME}"
        return 4
    fi
}

# Run the installer
run_installer() {
    print_step 7 8 "Running UTMStack installer"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would run installer"
        return 0
    fi
    
    if [[ ! -f "${INSTALLER_FILE}" ]]; then
        log_error "Installer file not found: ${INSTALLER_FILE}"
        return 1
    fi
    
    log_info "Executing installer..."
    echo "$(printf '=%.0s' $(seq 1 60))"
    
    if sudo ./"${INSTALLER_FILE}"; then
        echo "$(printf '=%.0s' $(seq 1 60))"
        log_success "Installer completed successfully"
    else
        local exit_code=$?
        echo "$(printf '=%.0s' $(seq 1 60))"
        log_error "Installer failed with exit code: ${exit_code}"
        return 1
    fi
}

# Monitor the update log
monitor_log() {
    print_step 8 8 "Monitoring update progress"
    
    if [[ "${DRY_RUN}" == true ]]; then
        log_info "DRY RUN: Would monitor log file"
        return 0
    fi
    
    log_info "Following log file: ${LOG_FILE}"
    log_info "Press Ctrl+C to exit log monitoring"
    echo "$(printf '=%.0s' $(seq 1 60))"
    
    # Wait for log file to be created
    local max_wait=10
    local wait_count=0
    
    while [[ ! -f "${LOG_FILE}" ]] && [[ $wait_count -lt $max_wait ]]; do
        log_verbose "Waiting for log file to be created... (${wait_count}s)"
        sleep 1
        ((wait_count++))
    done
    
    if [[ -f "${LOG_FILE}" ]]; then
        sudo tail -f "${LOG_FILE}"
    else
        log_error "Log file was not created after ${max_wait} seconds"
        log_info "Update may have failed. Check service status:"
        log_info "  sudo systemctl status ${SERVICE_NAME}"
        return 1
    fi
}

# ============================================================================
# Main Execution Function
# ============================================================================

main() {
    local exit_code=0
    
    print_header "UTMStack Update Process v${SCRIPT_VERSION}"
    log_info "Started at: ${TIMESTAMP}"
    log_info "Working directory: ${SCRIPT_DIR}"
    
    # Run pre-flight checks
    log_info "Running pre-flight checks..."
    check_privileges || exit $?
    check_dependencies || exit $?
    check_connectivity || exit $?
    check_installer_directory || exit $?
    log_success "Pre-flight checks completed"
    
    # Execute update steps
    remove_old_installer || exit_code=$?
    [[ $exit_code -ne 0 ]] && exit $exit_code
    
    download_installer || exit_code=$?
    [[ $exit_code -ne 0 ]] && exit $exit_code
    
    set_executable_permissions || exit_code=$?
    [[ $exit_code -ne 0 ]] && exit $exit_code
    
    stop_service || exit_code=$?
    [[ $exit_code -ne 0 ]] && exit $exit_code
    
    cleanup_log_file || exit_code=$?
    [[ $exit_code -ne 0 ]] && exit $exit_code
    
    start_service || exit_code=$?
    [[ $exit_code -ne 0 ]] && exit $exit_code
    
    run_installer || exit_code=$?
    [[ $exit_code -ne 0 ]] && exit $exit_code
    
    monitor_log || exit_code=$?
    
    if [[ $exit_code -eq 0 ]]; then
        print_header "Update Process Completed Successfully"
    else
        print_header "Update Process Completed with Errors"
    fi
    
    log_info "Finished at: $(date '+%Y-%m-%d %H:%M:%S')"
    exit $exit_code
}

# ============================================================================
# Script Entry Point
# ============================================================================

# Parse command line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -v|--verbose)
            VERBOSE=true
            shift
            ;;
        -d|--dry-run)
            DRY_RUN=true
            log_info "Running in DRY RUN mode - no changes will be made"
            shift
            ;;
        *)
            log_error "Unknown option: $1"
            log_info "Use -h or --help for usage information"
            exit 1
            ;;
    esac
done

# Trap errors and interrupts
trap 'log_error "Script interrupted"; exit 130' INT TERM
trap 'log_error "Script failed at line $LINENO with exit code $?"' ERR

# Execute main function
main "$@"
Save the file by pressing Ctrl + X, then Y, and Enter.
3

Make Script Executable

Grant execution permissions to the script:
chmod +x update.sh
Verify the script has execute permissions:
ls -lh update.sh
You should see -rwxr-xr-x at the beginning of the output.
4

Execute the Update Script

Run the update script with sudo privileges:
sudo bash update.sh
The script will:
  1. Perform pre-flight checks (privileges, dependencies, connectivity)
  2. Remove the old installer
  3. Download the latest installer from GitHub
  4. Set executable permissions
  5. Stop UTMStack services
  6. Clean up old log files
  7. Start UTMStack services
  8. Run the installer
  9. Monitor the update progress
Do not interrupt the update process. The update may take 10-30 minutes depending on your system specifications and network speed.
5

Monitor Update Progress

The script automatically monitors the update log. You’ll see real-time output as the update progresses.To exit the log monitoring, press Ctrl + C (this will not stop the update process).

Verification and Post-Update

After the update completes, verify the installation:
1

Check Update Logs

Review the update logs for any errors or warnings:
sudo tail -100 /utmstack/updates/logs/utmstack-updater.log
2

Verify Service Status

Confirm that all UTMStack services are running:
sudo systemctl status UTMStackComponentsUpdater
The service should show “active (running)” status.
3

Check Application Version

Verify the updated version through the UTMStack web interface:
  1. Log in to your UTMStack instance
  2. Navigate to SettingsAbout
  3. Confirm the version number matches the latest release
4

Test Functionality

Perform basic functionality tests:
  • Verify dashboard loads correctly
  • Check data ingestion is working
  • Test alert generation
  • Confirm integrations are functioning

Troubleshooting

Cause: The script is not being executed from the directory containing the UTMStack installer.Solution:
  1. Find your installer location:
    find /root /home -name 'installer' -type f 2>/dev/null
    
  2. Navigate to that directory:
    cd /path/to/installer/directory
    
  3. Run the script again
Cause: The server cannot reach GitHub to download the installer.Solution:
  1. Check network connectivity:
    ping -c 4 github.com
    
  2. Verify firewall rules allow outbound HTTPS (port 443)
  3. Check proxy settings if applicable
Cause: Service management issues with systemd.Solution:
  1. Check service status:
    sudo systemctl status UTMStackComponentsUpdater
    
  2. View service logs:
    sudo journalctl -u UTMStackComponentsUpdater -n 50
    
  3. Restart the service manually:
    sudo systemctl restart UTMStackComponentsUpdater
    
Cause: Network issues or resource constraints.Solution:
  1. Check system resources:
    df -h
    free -h
    
  2. Monitor the log file in another terminal:
    sudo tail -f /utmstack/updates/logs/utmstack-updater.log
    
  3. If necessary, stop and restart the update:
    sudo systemctl stop UTMStackComponentsUpdater
    sudo bash update.sh
    
Cause: Insufficient privileges to execute the script.Solution:
  • Always run the script with sudo:
    sudo bash update.sh
    
  • Verify you have root access:
    sudo whoami
    

Need Help? If you encounter issues during the update process, please contact UTMStack support or visit our community forums for assistance.