Deploying to shared hosting via FTP sounds archaic, but it's the reality for many PHP applications. Here's how I automated multi-server FTP deployment for TrendVidStream, a video platform running on 4 LiteSpeed servers.
The Problem
Manual FTP deployment to 4 servers is slow, error-prone, and tedious. I needed:
- Parallel deployment to all servers
- Automatic exclusion of sensitive files
- LiteSpeed cache clearing after deploy
- Post-deploy verification
The Solution: ops.sh
A bash script wrapping lftp for parallel FTP mirroring.
Configuration File
# deploy_hosts.conf
# CRITICAL: Must have Unix line endings (LF only, NO \r)
# format: alias|host|user|pass|remote_path
dwv|ftp.dailywatch.video|user1|pass1|/htdocs
tvh|ftp.topvideohub.com|user2|pass2|/htdocs
tvs|ftp.trendvidstream.com|user3|pass3|/htdocs
vvv|ftp.viralvidvault.com|user4|pass4|/htdocs
Deploy Script
#!/bin/bash
# ops.sh - Multi-server deployment automation
set -euo pipefail
CONFIG_FILE="deploy_hosts.conf"
EXCLUDE_PATTERNS=(
"--exclude .env"
"--exclude data/"
"--exclude '*.log'"
"--exclude .git/"
"--exclude .claude/"
)
validate_config() {
# Check for Windows line endings
if grep -qP '\r' "$CONFIG_FILE"; then
echo "ERROR: $CONFIG_FILE has Windows line endings!"
echo "Fix with: sed -i 's/\r$//' $CONFIG_FILE"
exit 1
fi
}
deploy_single() {
local alias="$1" host="$2" user="$3" pass="$4" path="$5"
echo "[$alias] Deploying..."
lftp -u "$user","$pass" "$host" -e "
set ssl:verify-certificate no;
mirror -R --verbose --ignore-time \
${EXCLUDE_PATTERNS[*]} \
./ $path;
rm -rf ${path}/lscache;
quit
" 2>&1 | while read line; do
echo "[$alias] $line"
done
echo "[$alias] Deploy complete + cache cleared"
}
deploy_all() {
validate_config
local pids=()
while IFS='|' read -r alias host user pass path; do
[[ -z "$alias" || "$alias" =~ ^#.*$ ]] && continue
deploy_single "$alias" "$host" "$user" "$pass" "$path" &
pids+=("$!")
done < "$CONFIG_FILE"
# Wait for all deployments
local failed=0
for pid in "${pids[@]}"; do
if ! wait "$pid"; then
((failed++))
fi
done
echo "Deployment complete. Failures: $failed"
}
case "${1:-}" in
deploy-all) deploy_all ;;
deploy) deploy_single "$2" ;;
*) echo "Usage: $0 {deploy-all|deploy <alias>}" ;;
esac
Critical Lessons
1. Line Endings Matter
# Verify line endings
cat -A deploy_hosts.conf
# Should see $ at end of lines, NOT ^M$
# Fix Windows line endings
sed -i 's/\r$//' deploy_hosts.conf
2. Always Use --ignore-time
Shared hosting FTP servers often have clock drift. Without --ignore-time, lftp uses timestamps to determine which files to transfer, and drifted clocks cause files to be skipped.
3. LiteSpeed vs Apache .htaccess
# Wrap LiteSpeed-specific rules so Apache skips them
<IfModule LiteSpeed>
CacheEnable public /
RewriteRule ^(.*)$ - [E=Cache-Control:max-age=10800]
</IfModule>
4. Post-Deploy Verification
verify_deploy() {
local host="$1" user="$2" pass="$3" path="$4"
local remote_hash=$(lftp -u "$user","$pass" "$host" -e "
cat ${path}/version.txt; quit
")
local local_hash=$(cat version.txt)
if [[ "$remote_hash" == "$local_hash" ]]; then
echo "Verification PASSED"
else
echo "Verification FAILED"
fi
}
This deployment system handles daily deploys to all 4 TrendVidStream servers reliably. The entire process takes under 5 minutes.
For shared hosting environments where SSH and CI/CD are not available, FTP automation with lftp is a pragmatic and reliable solution.
Why Not Just Use CI/CD?
The honest answer is: CI/CD requires SSH access, which shared hosting does not provide. Many developers dismiss shared hosting as outdated, but for PHP applications it offers an unbeatable price-to-performance ratio. LiteSpeed shared hosting plans at $10 per month deliver performance that rivals $50 per month VPS setups thanks to built-in caching.
Our FTP deployment toolkit bridges the gap between the simplicity of shared hosting and the reliability expectations of modern development. It is not as elegant as GitHub Actions with SSH deployment, but it solves the same problem: reliable, repeatable, automated deployments.
For anyone running PHP applications on shared hosting, this approach eliminates the biggest pain point. Manual FTP transfers are error-prone and time-consuming. Automated lftp mirroring with parallel execution, smart exclusions, and cache clearing turns deployment from a dreaded chore into a one-command operation.
The deployment automation for TrendVidStream has been running daily for months without a single deployment-related incident. Sometimes the pragmatic solution is the best solution.
Top comments (0)