Smarter Deployen: Ein universelles Bash-Skript für moderne Web-Apps mit lftp
Als Entwickler liebst du es, an deiner Web-App zu feilen – egal ob mit React, Vue, Svelte oder einem anderen modernen Framework. Das Deployment hingegen kann schnell zu einer lästigen, fehleranfälligen Routine werden.
Die moderne Webentwicklung predigt uns dafür eine klare Lösung: Continuous Integration und Continuous Deployment (CI/CD). Werkzeuge wie GitHub Actions, Azure DevOps Pipelines oder die Google Cloud Build-Plattform sind unglaublich mächtig und für große, kollaborative Projekte oft unerlässlich.
Aber Hand aufs Herz: Ist es wirklich immer die beste Lösung, für ein kleines bis mittelgroßes Projekt eine komplexe CI/CD-Pipeline aufzusetzen? Manchmal ist ein einfacher, transparenter und robuster Ansatz nicht nur ausreichend, sondern für den produktiven Einsatz sogar überlegen. Gerade wenn das Ziel ein klassischer Webspace ohne Git-Integration ist und die Bereitstellung per SFTP erfolgen muss, kann ein lokales Skript Gold wert sein. Es gibt dir die volle Kontrolle, ist blitzschnell eingerichtet und erfordert keine Abhängigkeit von externen Diensten oder komplexen YAML-Konfigurationen.
In diesem Beitrag zeige ich dir, wie du genau so einen smarten Weg gehst. Wir erstellen ein universelles Bash-Skript, das deinen Deployment-Prozess in einen einzigen, verlässlichen Befehl verwandelt: ./deploy.sh. Effizient, sicher und für fast jedes Projekt anpassbar.
Teil 1: Die Grundlage – Sichere lftp-Bookmarks einrichten (einmalig)
Anstatt Passwörter direkt in unser Skript zu schreiben (ein absolutes No-Go!), nutzen wir die Bookmark-Funktion von lftp. Wir speichern eine Verbindung unter einem Alias, und lftp kümmert sich sicher um die Anmeldedaten.
Wichtiger Sicherheitshinweis: lftp speichert das Passwort im Klartext in seiner Konfigurationsdatei (~/.local/share/lftp/bookmarks oder ~/.lftp/bookmarks). Diese Datei wird jedoch standardmäßig mit restriktiven Rechten (chmod 600) angelegt, sodass nur du sie lesen kannst. Dies ist ein gewaltiger Sicherheitsgewinn gegenüber einem Passwort im Skript und für die meisten Hosting-Umgebungen ein exzellenter Kompromiss.
Schritt 1: Manuelle Verbindung für den Setup Öffne dein Terminal und verbinde dich einmalig manuell mit deinem SFTP-Server. Ersetze die Platzhalter mit deinen Daten.
lftp -u dein-benutzername sftp://sftp.dein-server.delftp wird dich nach deinem Passwort fragen. Gib es ein und drücke Enter.
Schritt 2: Passwortspeicherung aktivieren Gib in der lftp-Kommandozeile (lftp :~>) folgenden Befehl ein. Er weist lftp an, bei der Erstellung des nächsten Bookmarks das Passwort mitzuspeichern.
lftp :~> set bmk:save-passwords trueSchritt 3: Bookmark speichern Speichere nun die Verbindung unter einem leicht merkbaren Namen. Diesen Namen brauchst du später im Skript.
lftp :~> bookmark add mein-projektSchritt 4: Beenden und Testen Verlasse lftp mit exit. Teste nun, ob der Bookmark funktioniert:
lftp mein-projektWenn du ohne Passwortabfrage verbunden wirst, hat alles geklappt! Verlasse die Sitzung wieder mit exit.
Teil 2: Das Herzstück – Das universelle deploy.sh-Skript
Speichere den folgenden Code in einer Datei namens deploy.sh im Hauptverzeichnis deines Projekts. Die Konfiguration ist so gestaltet, dass du sie leicht für dein spezifisches Framework anpassen kannst.
#!/bin/bash
#==============================================================================
# Simple WebApp SFTP Deployment Script with LFTP
# Version: 3.0
# Author: Daniel Wellermann / wellermann.de
#==============================================================================
# --- KONFIGURATION ----------------------------------------------------------
# Passe diese Variablen an dein Projekt und deine Serverumgebung an.
# PROJEKT-EINSTELLUNGEN
# Passe diese Befehle und Pfade an dein Framework an!
# ----------------------------------------------------------------------------
# Der Build-Befehl für dein Projekt.
# Beispiele:
# -> "npm run build" (Standard für viele Frameworks wie Vite, Vue CLI, Create React App)
# -> "npm run generate" (Standard für Nuxt)
# -> "astro build"
NODE_ENV="production"
NODE_COMMAND="npm run generate"
# Das lokale Verzeichnis, das nach dem Build hochgeladen werden soll.
# Beispiele:
# -> "dist" (Vite, Vue CLI, Astro)
# -> "build" (Create React App)
# -> ".output/public" (Nuxt 3 Static)
# -> "out" (Next.js Static Export)
LOCAL_DIR="dist"
# SERVER-EINSTELLUNGEN
# ----------------------------------------------------------------------------
# LFTP-Bookmark (BEVORZUGTE METHODE)
# Trage hier den Namen deines lftp-Bookmarks aus Teil 1 ein.
# Wenn dieser Wert gesetzt ist, werden die unteren SFTP-Daten ignoriert.
LFTP_BOOKMARK="mein-projekt"
# Zielverzeichnis auf dem Server (z.B. /httpdocs, /public_html, /var/www/html)
REMOTE_DIR="/"
# --- ODER: MANUELLE SFTP-DATEN (Fallback, falls kein Bookmark genutzt wird) ---
# Lasse LFTP_BOOKMARK leer, um diese Daten zu verwenden.
# ACHTUNG: Das Passwort wird bei jeder Ausführung interaktiv abgefragt.
HOST="sftp.dein-server.de"
USER="u123456"
PORT="22"
# --- ENDE DER KONFIGURATION -------------------------------------------------
# --- UI FUNKTIONEN ---------------------------------------------------
# Farben für die Ausgabe
C_GREEN="\033[0;32m"
C_RED="\033[0;31m"
C_BLUE="\033[0;34m"
C_YELLOW="\033[1;33m"
C_CYAN="\033[0;36m"
C_NC="\033[0m" # No Color
# banner und Statusmeldungen
print_banner() {
echo -e "${C_GREEN}"
cat << "EOF"
__ __ _ _ ____ _
\ \ / /__| |__ / \ _ __ _ __ | _ \ ___ _ __ | | ___ _ _
\ \ /\ / / _ \ '_ \ / _ \ | '_ \| '_ \ | | | |/ _ \ '_ \| |/ _ \| | | |
\ V V / __/ |_) / ___ \| |_) | |_) | | |_| | __/ |_) | | (_) | |_| |
\_/\_/ \___|_.__/_/ \_\ .__/| .__/ |____/ \___| .__/|_|\___/ \__, |
|_| |_| |_| |___/
EOF
echo -e "${C_CYAN} :: Simple WebApp Deploy SCRIPT :: ${C_NC}"
echo
}
print_step() {
echo -e "${C_YELLOW}[*] $1${C_NC}"
sleep 1 # Kurze Pause für den "Denk"-Effekt
}
print_success() {
echo -e "${C_GREEN}[✓] $1${C_NC}"
}
print_error() {
echo -e "${C_RED}[!] $1${C_NC}"
}
# --- ENDE DER UI FUNKTIONEN -------------------------------------------------
# --- SICHERHEIT -------------------------------------------------------------
# Sicherheitsabfrage für das Passwort falls kein Bookmark verwendet wird
if [[ -z "$LFTP_BOOKMARK" ]]; then
print_step "Sicherheitsabfrage: Passwort wird benötigt"
read -sp "Passwort für ${USER}@${HOST}: " PASSWORD
echo
if [[ -z "$PASSWORD" ]]; then
print_error "Kein Passwort eingegeben. Abbruch."
exit 1
fi
fi
# --- SKRIPT-LOGIK -----------------------------------------------------------
clear
print_banner
# 1. System-Check: Ist lftp installiert?
print_step "System-Check wird durchgeführt..."
if ! command -v lftp &> /dev/null; then
print_error "lftp nicht gefunden. Bitte installieren Sie es."
echo " -> Auf Debian/Ubuntu: sudo apt-get install lftp"
echo " -> Auf CentOS/RHEL: sudo yum install lftp"
echo " -> Auf macOS (Homebrew): brew install lftp"
exit 1
fi
print_success "System-Check erfolgreich. lftp ist verfügbar."
echo
# 1.5 Frage nach npm ci Ausführung
read -p "Möchten Sie 'npm ci' ausführen, um die Abhängigkeiten zu installieren? (j/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
print_step "Führe 'npm ci' aus..."
echo -e "${C_BLUE}----------------------------------------------------------------------${C_NC}"
echo -e "${C_CYAN} npm ci Ausgabe:${C_NC}"
echo -e "${C_BLUE}----------------------------------------------------------------------${C_NC}"
npm ci
NPM_CI_EXIT_CODE=$?
echo -e "${C_BLUE}----------------------------------------------------------------------${C_NC}"
if [ $NPM_CI_EXIT_CODE -ne 0 ]; then
print_error "npm ci fehlgeschlagen! Bitte überprüfen Sie die Fehler oben."
exit 1
fi
print_success "Abhängigkeiten wurden erfolgreich installiert."
echo
fi
# 2. Build-Prozess: Führe npm build aus
print_step "Starte Build-Prozess mit ${NODE_COMMAND}..."
echo -e "${C_BLUE}----------------------------------------------------------------------${C_NC}"
echo -e "${C_CYAN} Build-Ausgabe:${C_NC}"
echo -e "${C_BLUE}----------------------------------------------------------------------${C_NC}"
# Setze NODE_ENV und führe den Build-Befehl aus
NODE_ENV=$NODE_ENV $NODE_COMMAND
BUILD_EXIT_CODE=$?
echo -e "${C_BLUE}----------------------------------------------------------------------${C_NC}"
if [ $BUILD_EXIT_CODE -ne 0 ]; then
print_error "Build-Prozess fehlgeschlagen! Bitte überprüfen Sie die Fehler oben."
exit 1
fi
print_success "Build-Prozess erfolgreich abgeschlossen."
echo
# 3. Konfigurations-Check: Existiert das lokale Verzeichnis?
print_step "Überprüfe Konfiguration und lokale Dateien..."
if [ ! -d "$LOCAL_DIR" ]; then
print_error "Lokales Verzeichnis '${LOCAL_DIR}' nicht gefunden!"
echo " -> Wurde der Build-Prozess korrekt ausgeführt?"
exit 1
fi
print_success "Lokales Verzeichnis '${LOCAL_DIR}' gefunden. Bereit zum Upload."
echo -e "${C_BLUE}======================================================================${C_NC}"
echo -e "${C_CYAN} Deployment-Zusammenfassung:${C_NC}"
echo -e "${C_BLUE}----------------------------------------------------------------------${C_NC}"
echo -e " - ${C_YELLOW}Lokaler Ordner:${C_NC} ${LOCAL_DIR}"
echo -e " - ${C_YELLOW}Server:${C_NC} ${HOST}:${PORT}"
echo -e " - ${C_YELLOW}Benutzer:${C_NC} ${USER}"
echo -e " - ${C_YELLOW}Zielordner:${C_NC} ${REMOTE_DIR}"
echo -e "${C_BLUE}======================================================================${C_NC}"
echo
read -p "Soll der Upload gestartet werden? (j/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Jj]$ ]]; then
print_error "Abbruch durch Benutzer."
exit 1
fi
echo
# 4. Starte den LFTP Mirror-Prozess
print_step "Verbindung zum SFTP-Server wird aufgebaut..."
print_step "Starte Synchronisation (Mirror)... Dies kann einen Moment dauern."
# Der LFTP Befehl
# -u user,pass: Setzt Benutzer und Passwort
# sftp://host:port: Legt das Protokoll, den Host und den Port fest
# -e '...': Führt die folgenden Befehle in lftp aus
# set sftp:auto-confirm yes: Bestätigt neue SSH-Host-Keys automatisch
# mirror -R --delete --verbose --parallel=5: Der eigentliche Spiegel-Befehl
# -R, --reverse: Upload von lokal nach remote
# --delete: Löscht Dateien auf dem Server, die lokal nicht existieren
# --verbose: Zeigt jede Dateiübertragung an (perfekt für die Hacker-UI!)
# --parallel=5: Bis zu 5 Dateien gleichzeitig hochladen für mehr Speed
# quit: Beendet die lftp-Sitzung sauber
if [[ -n "$LFTP_BOOKMARK" ]]; then
print_step "Verwende lftp-Bookmark: ${LFTP_BOOKMARK}"
lftp -c "open ${LFTP_BOOKMARK}; \
set sftp:auto-confirm yes; \
mirror -R --delete --verbose --parallel=5 '${LOCAL_DIR}' '${REMOTE_DIR}'; \
quit"
else
print_step "Keine lftp-Bookmarks verwendet. Direktverbindung zum Server."
lftp -u "${USER},${PASSWORD}" sftp://"${HOST}":"${PORT}" \
-e "set sftp:auto-confirm yes; \
mirror -R --delete --verbose --parallel=5 '${LOCAL_DIR}' '${REMOTE_DIR}'; \
quit"
fi
# 5. Ergebnis auswerten
if [ $? -eq 0 ]; then
echo
print_success "DEPLOYMENT ERFOLGREICH!"
echo -e "${C_CYAN}Die Seite wurde erfolgreich auf ${HOST} aktualisiert.${C_NC}"
echo
echo -e "${C_GREEN}"
cat << "EOF"
____ __ __
| _ \ ___ _ __ ___ | _|_ _|_ |
| | | |/ _ \| '_ \ / _ \ | |\ \ /\ / /| |
| |_| | (_) | | | | __/ | | \ V V / | |
|____/ \___/|_| |_|\___| | | \_/\_/ | |
|__| |__|
EOF
echo -e "${C_CYAN} :: Besuch gerne wellermann.de :: ${C_NC}"
echo -e "${C_NC}"
else
echo
print_error "DEPLOYMENT FEHLGESCHLAGEN!"
echo -e "${C_RED}Beim Hochladen sind Fehler aufgetreten. Bitte prüfen Sie die Ausgabe oben.${C_NC}"
fi
exit 0Teil 3: Anwendung in der Praxis
Jetzt, wo alles vorbereitet ist, ist das Deployment ein Kinderspiel.
Skript konfigurieren:
- Öffne die Datei
deploy.sh. - Passe die
PROJEKT-EINSTELLUNGENan! Überprüfe die Kommentare im Skript und setze den korrektenNODE_COMMANDundLOCAL_DIRfür dein spezifisches Framework. - Trage bei
LFTP_BOOKMARKden Namen ein, den du in Teil 1 gewählt hast. - Setze das
REMOTE_DIRauf den Zielordner deines Webservers (z.B.httpdocs).
- Öffne die Datei
Skript ausführbar machen: Führe diesen Befehl einmalig im Terminal aus, um dem System zu erlauben, das Skript zu starten:
bashchmod +x deploy.shDeployment starten: Ab sofort ist dein gesamter Deployment-Prozess nur noch einen Befehl entfernt. Führe einfach aus:
bash./deploy.sh
Das Skript kümmert sich um alles Weitere: Es baut deine Anwendung, stellt eine sichere Verbindung her und synchronisiert die Dateien auf deinen Server. Effizient, sicher und für fast jedes Projekt anpassbar. Viel Spaß beim Coden