DEV Community

ahmet gedik
ahmet gedik

Posted on

FTP-Based Multi-Server Deployment Automation

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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)