Could we help you? Please click the banners. We are young and desperately need the money
Mattermost, a popular open-source alternative to proprietary messaging platforms like Slack, requires regular updates to maintain security patches and access new features. While the update process isn't particularly complex, it involves several steps that can be error-prone when performed manually:
Our script automates this entire workflow while adding intelligent features such as disk space verification, backup controls, and edition selection.
Before diving into the code, let's explore the key features that make this script valuable for system administrators:
These features make the script suitable for both production environments and personal servers, regardless of the administrator's experience level.
To use this script effectively, you'll need:
curl
, wget
, tar
, systemctl
Most Linux distributions include these tools by default, but the script checks for their presence and will notify you if anything is missing.
Here's the full script with detailed comments explaining each section:
#!/bin/bash
# =================================================================
# Mattermost Automatic Update Script
# =================================================================
# Configuration variables - modify these to match your installation
# =================================================================
# Base installation directory
MATTERMOST_BASE_DIR="/opt"
MATTERMOST_DIR="${MATTERMOST_BASE_DIR}/mattermost"
# Temporary directory for downloads and extraction
TEMP_DIR="/tmp"
# User and group that should own the Mattermost installation
MATTERMOST_USER="mattermost"
MATTERMOST_GROUP="mattermost"
# Mattermost service name
MATTERMOST_SERVICE="mattermost"
# Mattermost edition - "enterprise" or "team"
MATTERMOST_EDITION="enterprise" # Set to "team" if you're using the Team Edition
# Backup settings
CREATE_BACKUP=true # Set to false to skip backup creation
BACKUP_DIR_PREFIX="mattermost-backup" # Prefix for backup directory name - will be appended with date/time (e.g., mattermost-backup-20240430-120135)
BACKUP_STORAGE_PATH="${MATTERMOST_BASE_DIR}" # Where backups will be stored (defaults to same as installation directory)
# Directories to preserve during update
PRESERVE_DIRS=(
"client"
"client/plugins"
"config"
"logs"
"plugins"
"data"
)
# Number of log lines to show after restart
LOG_LINES=20
# Set to true to follow logs, false to just display the last LOG_LINES
FOLLOW_LOGS=true
# =================================================================
# Script logic - You shouldn't need to modify below this line
# =================================================================
# Enable exit on error
set -e
# Color codes for better readability
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function for timestamped logging
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
info() {
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1${NC}"
}
debug() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] $1${NC}"
}
warn() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $1${NC}" >&2
}
error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1${NC}" >&2
}
# Function to check command status
check_status() {
if [ $? -ne 0 ]; then
error "$1"
exit 1
fi
}
# Function to check if running as root
check_root() {
if [ "$(id -u)" != "0" ]; then
error "This script must be run as root or with sudo"
exit 1
fi
}
# Check for required commands
check_dependencies() {
local missing_deps=()
for cmd in curl wget tar systemctl; do
if ! command -v $cmd &> /dev/null; then
missing_deps+=($cmd)
fi
done
if [ ${#missing_deps[@]} -ne 0 ]; then
error "Missing required dependencies: ${missing_deps[*]}"
echo "Please install the missing dependencies and try again."
exit 1
fi
}
# Check if Mattermost is installed
check_mattermost_installed() {
if [ ! -d "$MATTERMOST_DIR" ]; then
error "Mattermost installation not found at $MATTERMOST_DIR"
exit 1
fi
if ! systemctl status $MATTERMOST_SERVICE &> /dev/null; then
error "Mattermost service ($MATTERMOST_SERVICE) not found"
exit 1
fi
}
# Function to check Mattermost edition validity
check_edition() {
if [[ ! "$MATTERMOST_EDITION" =~ ^(enterprise|team)$ ]]; then
error "Invalid MATTERMOST_EDITION value: $MATTERMOST_EDITION"
echo "MATTERMOST_EDITION must be either 'enterprise' or 'team'"
exit 1
fi
}
# Function to check available disk space
check_disk_space() {
local source_dir="$1"
local target_dir="$2"
# Get size of source directory in KB
local source_size=$(du -sk "$source_dir" | cut -f1)
# Get available space on target device in KB
local target_avail=$(df -kP "$target_dir" | tail -1 | awk '{print $4}')
# Convert to MB for display
local source_size_mb=$(($source_size / 1024))
local target_avail_mb=$(($target_avail / 1024))
# Return values via global variables
DISK_CHECK_SOURCE_SIZE_MB=$source_size_mb
DISK_CHECK_TARGET_AVAIL_MB=$target_avail_mb
# Check if there's enough space (plus 10% buffer)
local required_space=$(($source_size * 110 / 100))
if [ $target_avail -lt $required_space ]; then
return 1
else
return 0
fi
}
# Initialize the script
initialize() {
log "Initializing Mattermost update script"
check_root
check_dependencies
check_mattermost_installed
check_edition
}
# Main script
initialize
# Create the upgrade directory name
UPGRADE_DIR="mattermost-upgrade"
TEMP_UPGRADE_DIR="${TEMP_DIR}/${UPGRADE_DIR}"
# Fetch latest version URL based on selected edition
log "Fetching Mattermost ${MATTERMOST_EDITION} edition release information..."
DOWNLOAD_URL=$(curl -s https://releases.mattermost.com/ | grep -o "https://.*${MATTERMOST_EDITION}.*linux-amd64\.tar\.gz" | sort -V | tail -n1)
check_status "Failed to fetch release information"
if [ -z "$DOWNLOAD_URL" ]; then
error "Could not find ${MATTERMOST_EDITION} edition Linux release URL"
exit 1
fi
VERSION=$(basename "$DOWNLOAD_URL")
echo
echo -e "${YELLOW}---------------------------------------------------------------${NC}"
echo -e "${YELLOW}Found latest Mattermost ${MATTERMOST_EDITION^} edition version: ${GREEN}$VERSION${NC}"
echo -e "${YELLOW}---------------------------------------------------------------${NC}"
echo -n "Do you want to upgrade Mattermost to this version? (y/n): "
read -r REPLY
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log "Upgrade cancelled by user"
exit 0
fi
# Change to temporary directory
log "Changing to temporary directory ($TEMP_DIR)..."
cd $TEMP_DIR || {
error "Failed to change to temporary directory"
exit 1
}
# Clean up any previous failed attempts
if [ -d "$TEMP_UPGRADE_DIR" ]; then
info "Cleaning up previous upgrade directory..."
rm -rf "$TEMP_UPGRADE_DIR"
fi
if [ -f "${TEMP_DIR}/mattermost*.gz" ]; then
info "Cleaning up previous downloaded archive..."
rm -f ${TEMP_DIR}/mattermost*.gz
fi
# Download the release
log "Downloading Mattermost from $DOWNLOAD_URL..."
wget -q --show-progress "$DOWNLOAD_URL"
check_status "Failed to download Mattermost"
ARCHIVE_FILE=$(basename "$DOWNLOAD_URL")
log "Downloaded $ARCHIVE_FILE successfully"
# Extract the archive
log "Extracting archive..."
tar -xf "$ARCHIVE_FILE" --transform="s,^[^/]\+,${UPGRADE_DIR}," || {
error "Failed to extract archive"
exit 1
}
# Stop Mattermost service
log "Stopping Mattermost service..."
systemctl stop $MATTERMOST_SERVICE || {
error "Failed to stop Mattermost service"
exit 1
}
# Backup current version - if enabled
if [ "$CREATE_BACKUP" = true ]; then
# Check if backup path exists, create if it doesn't
if [ ! -d "$BACKUP_STORAGE_PATH" ]; then
log "Creating backup storage directory $BACKUP_STORAGE_PATH..."
mkdir -p "$BACKUP_STORAGE_PATH" || {
error "Failed to create backup storage directory"
exit 1
}
fi
# Check available disk space
log "Checking available disk space for backup..."
if ! check_disk_space "$MATTERMOST_DIR" "$BACKUP_STORAGE_PATH"; then
warn "WARNING: Not enough disk space for backup!"
echo -e "${YELLOW}The Mattermost directory is ${DISK_CHECK_SOURCE_SIZE_MB} MB in size${NC}"
echo -e "${YELLOW}Available space at backup location is only ${DISK_CHECK_TARGET_AVAIL_MB} MB${NC}"
echo -e "${YELLOW}Recommended space (with 10% buffer): $((DISK_CHECK_SOURCE_SIZE_MB * 110 / 100)) MB${NC}"
echo
echo -n "Do you want to proceed with the backup anyway? (y/n): "
read -r REPLY_BACKUP
if [[ ! $REPLY_BACKUP =~ ^[Yy]$ ]]; then
log "Backup cancelled due to insufficient space"
echo -n "Do you want to continue with the update without creating a backup? (y/n): "
read -r REPLY_UPDATE
if [[ ! $REPLY_UPDATE =~ ^[Yy]$ ]]; then
log "Update cancelled by user"
exit 0
else
info "Proceeding with update without backup"
CREATE_BACKUP=false
fi
else
info "Proceeding with backup despite space warning"
fi
else
info "Sufficient disk space available (${DISK_CHECK_SOURCE_SIZE_MB} MB needed, ${DISK_CHECK_TARGET_AVAIL_MB} MB available)"
fi
if [ "$CREATE_BACKUP" = true ]; then
BACKUP_DIR="${BACKUP_STORAGE_PATH}/${BACKUP_DIR_PREFIX}-$(date +%Y%m%d-%H%M%S)"
log "Creating backup at $BACKUP_DIR..."
cp -a "$MATTERMOST_DIR" "$BACKUP_DIR" || {
error "Failed to create backup"
exit 1
}
info "Backup created successfully at $BACKUP_DIR"
fi
else
info "Backup creation skipped (CREATE_BACKUP=$CREATE_BACKUP)"
fi
# Change to base directory
log "Changing to Mattermost base directory ($MATTERMOST_BASE_DIR)..."
cd $MATTERMOST_BASE_DIR || {
error "Failed to change to Mattermost base directory"
exit 1
}
# Create preserve paths list for removal exclusion
PRESERVE_PATHS=""
for dir in "${PRESERVE_DIRS[@]}"; do
PRESERVE_PATHS+=" -o -path $MATTERMOST_DIR/$dir"
done
PRESERVE_PATHS=${PRESERVE_PATHS:3} # Remove the first " -o"
# Remove old files but preserve specific directories
log "Removing old files (preserving data directories)..."
find $MATTERMOST_DIR/ $MATTERMOST_DIR/client/ -mindepth 1 -maxdepth 1 \! \( -type d \( $PRESERVE_PATHS \) -prune \) | sort | xargs rm -rf || {
error "Failed to remove old files"
exit 1
}
# Copy new files
log "Copying new files..."
cp -a --update=none "${TEMP_DIR}/${UPGRADE_DIR}/." $MATTERMOST_DIR/ || {
error "Failed to copy new files"
exit 1
}
# Set ownership
log "Setting ownership to $MATTERMOST_USER:$MATTERMOST_GROUP..."
chown -R $MATTERMOST_USER:$MATTERMOST_GROUP $MATTERMOST_DIR || {
error "Failed to set ownership"
exit 1
}
# Set capabilities
log "Setting required capabilities..."
setcap cap_net_bind_service=+ep $MATTERMOST_DIR/bin/mattermost || {
error "Failed to set capabilities"
exit 1
}
# Cleanup
log "Cleaning up temporary files..."
rm -rf "${TEMP_DIR}/${UPGRADE_DIR}/" || {
error "Failed to remove temporary upgrade directory"
exit 1
}
log "Removing downloaded archive..."
rm -rf "${TEMP_DIR}/${ARCHIVE_FILE}" || {
error "Failed to remove downloaded archive"
exit 1
}
# Reminder for plugin updates
echo
echo -e "${YELLOW}---------------------------------------------------------------${NC}"
echo -e "${YELLOW}REMINDER: Update Mattermost Boards plugin if needed${NC}"
echo -e "${YELLOW}https://github.com/mattermost/mattermost-plugin-boards/releases/${NC}"
echo -e "${YELLOW}---------------------------------------------------------------${NC}"
echo
# Start Mattermost service
log "Starting Mattermost service..."
systemctl start $MATTERMOST_SERVICE || {
error "Failed to start Mattermost service"
exit 1
}
# Wait a moment for the service to start
sleep 3
# Display logs
log "Mattermost updated successfully to version $VERSION"
log "Displaying Mattermost logs..."
if [ "$FOLLOW_LOGS" = true ]; then
tail -n $LOG_LINES -F $MATTERMOST_DIR/logs/mattermost.log || {
error "Failed to display logs"
exit 1
}
else
tail -n $LOG_LINES $MATTERMOST_DIR/logs/mattermost.log || {
error "Failed to display logs"
exit 1
}
fi
exit 0
Let's break down the key components of the script to understand how it works:
The top section contains all customizable variables:
# Base installation directory
MATTERMOST_BASE_DIR="/opt"
MATTERMOST_DIR="${MATTERMOST_BASE_DIR}/mattermost"
# User and group that should own the Mattermost installation
MATTERMOST_USER="mattermost"
MATTERMOST_GROUP="mattermost"
# Mattermost edition - "enterprise" or "team"
MATTERMOST_EDITION="enterprise"
# Backup settings
CREATE_BACKUP=true
BACKUP_DIR_PREFIX="mattermost-backup"
BACKUP_STORAGE_PATH="${MATTERMOST_BASE_DIR}"
These variables make the script highly adaptable to different server configurations without modifying the core logic.
The script includes several logging functions that provide clear, color-coded output:
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
info() {
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1${NC}"
}
warn() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $1${NC}" >&2
}
error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1${NC}" >&2
}
These functions make it easy to distinguish between different types of messages and quickly identify problems.
Before performing any actions, the script verifies:
This prevents common issues before they occur.
One of the most useful features is the disk space verification:
# Check available disk space
log "Checking available disk space for backup..."
if ! check_disk_space "$MATTERMOST_DIR" "$BACKUP_STORAGE_PATH"; then
warn "WARNING: Not enough disk space for backup!"
# ...warning details and user prompt...
fi
This prevents the frustrating scenario of a backup failing midway due to insufficient space, potentially leaving your server in an inconsistent state.
The actual update process follows these steps:
Before using the script, you should adjust the following variables to match your environment:
MATTERMOST_BASE_DIR
: The base directory where Mattermost is installedMATTERMOST_DIR
: The specific Mattermost directoryMATTERMOST_USER
: The user that owns Mattermost filesMATTERMOST_GROUP
: The group that owns Mattermost filesMATTERMOST_SERVICE
: The name of your Mattermost systemd serviceMATTERMOST_EDITION
: Choose "enterprise" or "team" based on your installationCREATE_BACKUP
: Set to true/false to enable/disable backupsBACKUP_DIR_PREFIX
: Customize the backup directory namingBACKUP_STORAGE_PATH
: Specify where backups should be storedWhen it comes to updating Mattermost, administrators have several options. Let's compare this script with other common methods:
Feature | This Script | Manual Update | Docker Update | Package Manager |
---|---|---|---|---|
Automation | Full | None | Partial | Full |
Backup Creation | Configurable | Manual | Manual | Limited |
Disk Space Check | Yes | Manual | No | Limited |
Edition Selection | Yes | Manual | Tag-based | Package-specific |
Customization | High | Complete | Limited | Very Limited |
Error Handling | Comprehensive | Manual | Limited | Varies |
Data Preservation | Configurable | Manual | Volume-based | Package Rules |
As the comparison shows, this script offers the highest level of automation while maintaining flexibility and adding safety features like disk space verification that aren't available in other methods.
This script is particularly valuable in the following scenarios:
For organizations with scheduled maintenance windows, the script can be triggered at the appropriate time to perform updates efficiently and consistently.
If you manage multiple Mattermost instances (e.g., for different departments or environments), the script ensures a consistent update process across all servers.
The disk space verification is especially valuable in environments with limited storage, preventing failed updates due to insufficient space.
For servers with high uptime requirements, the script's error handling and backup capabilities minimize the risk of prolonged downtime during updates.
To get the most out of this script, consider the following best practices:
Before running the script on your production server, test it in a staging environment to ensure it works correctly with your specific configuration.
Set up a regular schedule for updates to ensure you receive security patches and bug fixes promptly.
While the script creates backups automatically, you should implement a rotation policy to prevent filling up your disk with old backups. Consider adding a step to move older backups to archival storage.
Even with automated updates, it's important to check the Mattermost logs after an update to ensure everything is functioning correctly. The script automatically issues a TAIL command on the Mattermost Log right after restarting the service: