Summary
To streamline server maintenance, we built two bash scripts for CrushFTP management. The first automates in-place updates through the built-in API, ensuring servers stay patched with minimal downtime. The second handles major version upgrades (v10 to v11), migrating all critical settings, user data, SSH keys, jobs, and custom branding — turning a complex manual process into a single reliable command.
The challenge
Manually updating and upgrading CrushFTP servers is repetitive and error-prone. In-place updates mean logging into the admin console, clicking through prompts, waiting, verifying. Major version upgrades are worse: you're copying a dozen folders and config files by hand, uninstalling the old daemon, installing the new one, and hoping nothing got missed. Done across multiple environments, this is exactly the kind of task where one tired copy-paste turns into a 2 AM support ticket.
The ask was simple: make both operations automated, consistent, and safe enough to run on a cron schedule.
Solution: automation scripts
Two distinct bash scripts — one for minor in-place updates, one for the v10 to v11 major version migration. Different goals, different designs.
Script 1: automated in-place updates
This one leverages the CrushFTP HTTPS API to check for and apply pending updates. Designed to run as a cron job so servers stay current with security patches and bug fixes automatically. Every action writes to /var/log/crushftp_update.log for later auditing.
It hits /?command=checkForUpdate, parses the XML response, and only fires /?command=updateNow if an update is actually available — no unnecessary restarts, no noise when there's nothing to do.
#!/bin/bash
LOG_FILE="/var/log/crushftp_update.log"
CHECK_UPDATE_URL="https://127.0.0.1:443/?command=checkForUpdate"
UPDATE_URL="https://127.0.0.1:443/?command=updateNow"
USERNAME="crushadmin"
PASSWORD="YOUR_SECRET_PASSWORD" # Use vault in production
{
echo "Starting CrushFTP update check..."
echo "Date/Time: $(date)"
RESPONSE=$(curl -k -s -u $USERNAME:$PASSWORD "$CHECK_UPDATE_URL")
RESULT=$(echo "$RESPONSE" | grep -oPm1 "(?<=<response>)[^<]+")
if [ "$RESULT" == "true" ]; then
echo "Update available! Updating..."
UPDATE_RESPONSE=$(curl -k -s -u $USERNAME:$PASSWORD "$UPDATE_URL")
UPDATE_RESULT=$(echo "$UPDATE_RESPONSE" | grep -oPm1 "(?<=<response>)[^<]+")
if [ "$UPDATE_RESULT" == "Success" ]; then
echo "Update successfully completed!"
else
echo "Update process failed!"
fi
else
echo "No updates available."
fi
echo "CrushFTP update check completed."
} 2>&1 | tee -a "$LOG_FILE"Script 2: major version upgrade (V10 to v11)
The major upgrade is where manual work really bites. CrushFTP's data is scattered across a lot of folders — users, prefs.XML, SSH host keys, TempAccounts, Previews, logs, jobs (enterprise only), statsDB — and if you miss any of them, the new install comes up wrong in ways that aren't always obvious until a user tries to log in.
This script handles the whole thing: downloads the new version, extracts it next to the old install, copies everything that matters, preserves the custom web interface logo, uninstalls the old daemon, installs the new one.
#!/bin/bash
# Define the old and new CrushFTP installation paths
OLD_CRUSH_PATH="/var/opt/CrushFTP10"
NEW_CRUSH_PATH="/var/opt/CrushFTP11"
# Download the CrushFTP 11 package
echo "Downloading CrushFTP 11..."
wget https://www.crushftp.com/early11/CrushFTP11.zip -O "$NEW_CRUSH_PATH.zip"
# Unzip the package
echo "Unzipping CrushFTP 11 package..."
unzip "$NEW_CRUSH_PATH.zip" -d /var/opt
# Make the initialization script executable
echo "Making initialization script executable..."
chmod +x "$NEW_CRUSH_PATH/crushftp_init.sh"
# Copy over the necessary files and folders
echo "Copying users, prefs, keys, and other critical files..."
cp -R "$OLD_CRUSH_PATH/users" "$NEW_CRUSH_PATH/"
cp "$OLD_CRUSH_PATH/prefs.XML" "$NEW_CRUSH_PATH/"
cp "$OLD_CRUSH_PATH/ssh_host_dsa_key" "$NEW_CRUSH_PATH/"
cp "$OLD_CRUSH_PATH/ssh_host_rsa_key" "$NEW_CRUSH_PATH/"
cp "$OLD_CRUSH_PATH/ssh_host_dsa_key.pub" "$NEW_CRUSH_PATH/"
cp "$OLD_CRUSH_PATH/ssh_host_rsa_key.pub" "$NEW_CRUSH_PATH/"
cp -R "$OLD_CRUSH_PATH/TempAccounts" "$NEW_CRUSH_PATH/"
cp -R "$OLD_CRUSH_PATH/Previews" "$NEW_CRUSH_PATH/"
cp -R "$OLD_CRUSH_PATH/logs" "$NEW_CRUSH_PATH/"
cp -R "$OLD_CRUSH_PATH/jobs" "$NEW_CRUSH_PATH/"
cp -R "$OLD_CRUSH_PATH/statsDB" "$NEW_CRUSH_PATH/"
# Copy custom logo
cp "$OLD_CRUSH_PATH/WebInterface/images/logo.png" "$NEW_CRUSH_PATH/WebInterface/images/"
# Remove old version daemon launch references
echo "Removing old version daemon launch references..."
cd "$NEW_CRUSH_PATH"
./crushftp_init.sh uninstall
# Install new version daemon launch references
echo "Installing new version daemon launch references..."
./crushftp_init.sh install
echo "CrushFTP 11 upgrade complete. Verify services are running."Security considerations
The in-place update script needs an admin username and password to talk to the API. We've sanitized the version shown here for public view — in production, never hardcode credentials directly.
- use a secret store (HashiCorp Vault, AWS Secrets Manager, or environment variables injected at runtime) to supply the password
- lock down script permissions with chmod 700 so only the owner can read or execute
- license registration for the major upgrade is still a manual step after the script completes — that's intentional
Outcome
These scripts cut the time and effort to maintain CrushFTP infrastructure way down. What used to be a multi-step manual process is now a single reliable command that can be scheduled without worry.
- drastically reduced manual effort for updates and upgrades
- consistent application of updates across all environments
- minimizes risk of human error during migrations (the copy step is where people always miss something)
- cron-job compatibility means "set-and-forget" security patching