Back to Blog
git deployment automation

Git Hooks für automatisches Deployment

CI/CD ist toll, aber manchmal overkill. Für kleine Projekte reicht ein Git Hook.

Das Konzept

  1. Du pushst zu einem Git-Repo auf deinem Server
  2. Ein Hook triggert
  3. Deine App wird deployed

Einfach. Schnell. Keine externe Abhängigkeit.

Bare Repository einrichten

Auf dem Server:

mkdir -p /var/repo/myapp.git
cd /var/repo/myapp.git
git init --bare

Post-Receive Hook

Der Magic passiert hier. /var/repo/myapp.git/hooks/post-receive:

#!/bin/bash
set -e

TARGET="/var/www/myapp"
GIT_DIR="/var/repo/myapp.git"
BRANCH="main"

while read oldrev newrev ref
do
    if [ "$ref" = "refs/heads/$BRANCH" ]; then
        echo ">>> Deploying $BRANCH to $TARGET"

        # Checkout
        git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH

        # Build (optional)
        cd $TARGET
        if [ -f "package.json" ]; then
            echo ">>> Installing dependencies"
            npm ci --production
        fi

        # Restart (optional)
        if [ -f "docker-compose.yml" ]; then
            echo ">>> Restarting containers"
            docker compose up -d --build
        fi

        echo ">>> Deployment complete!"
    fi
done
chmod +x /var/repo/myapp.git/hooks/post-receive

Lokal: Remote hinzufügen

git remote add deploy user@server:/var/repo/myapp.git

Deployen

git push deploy main

Das war's. Push = Deploy.

Erweitertes Beispiel

Mit Downtime-Minimierung

#!/bin/bash
set -e

TARGET="/var/www/myapp"
TARGET_NEW="/var/www/myapp-new"
TARGET_OLD="/var/www/myapp-old"
GIT_DIR="/var/repo/myapp.git"
BRANCH="main"

while read oldrev newrev ref
do
    if [ "$ref" = "refs/heads/$BRANCH" ]; then
        echo ">>> Deploying $BRANCH"

        # In neues Verzeichnis auschecken
        rm -rf $TARGET_NEW
        mkdir -p $TARGET_NEW
        git --work-tree=$TARGET_NEW --git-dir=$GIT_DIR checkout -f $BRANCH

        # Build
        cd $TARGET_NEW
        npm ci --production
        npm run build

        # Atomic swap
        rm -rf $TARGET_OLD
        mv $TARGET $TARGET_OLD 2>/dev/null || true
        mv $TARGET_NEW $TARGET

        # Restart
        sudo systemctl reload nginx

        echo ">>> Done!"
    fi
done

Mit Docker Compose

#!/bin/bash
set -e

APP_DIR="/opt/myapp"

while read oldrev newrev ref
do
    if [ "$ref" = "refs/heads/main" ]; then
        echo ">>> Updating code"
        git --work-tree=$APP_DIR checkout -f main

        cd $APP_DIR

        echo ">>> Pulling new images"
        docker compose pull

        echo ">>> Rebuilding and restarting"
        docker compose up -d --build --remove-orphans

        echo ">>> Cleaning up"
        docker image prune -f

        echo ">>> Done!"
    fi
done

Mit Benachrichtigung

#!/bin/bash
set -e

# ... deployment code ...

# Slack Notification
curl -X POST -H 'Content-type: application/json' \
  --data "{\"text\":\"Deployed $newrev to production\"}" \
  $SLACK_WEBHOOK_URL

Mehrere Branches

while read oldrev newrev ref
do
    branch=$(echo $ref | cut -d/ -f3)

    case $branch in
        main)
            TARGET="/var/www/production"
            ;;
        staging)
            TARGET="/var/www/staging"
            ;;
        *)
            echo ">>> Ignoring branch $branch"
            continue
            ;;
    esac

    echo ">>> Deploying $branch to $TARGET"
    # ... rest of deployment
done

Rollback

Da es ein Git-Repo ist:

# Auf dem Server
cd /var/www/myapp
git log --oneline -5
git checkout HEAD~1

# Oder lokal
git revert HEAD
git push deploy main

Sicherheit

Nur bestimmte User

# Im Hook
ALLOWED_USERS="daniel deploy"
if [[ ! " $ALLOWED_USERS " =~ " $USER " ]]; then
    echo ">>> Deployment not allowed for $USER"
    exit 1
fi

Validierung

# Tests laufen lassen
npm test || {
    echo ">>> Tests failed, aborting deployment"
    exit 1
}

Vorteile

  • Einfach: Keine externe Infrastruktur
  • Schnell: Kein CI/CD Queue
  • Transparent: Alles in einem Script
  • Flexibel: Bash kann alles

Nachteile

  • Keine Parallelisierung: Ein Deploy nach dem anderen
  • Server-Abhängig: Hook läuft auf dem Ziel-Server
  • Kein Review-Prozess: Direkt in Prod möglich

Wann nutzen?

  • Kleine Projekte
  • Personal Projects
  • Wenn CI/CD Overkill ist
  • Schnelle Iteration

Wann NICHT nutzen?

  • Team-Projekte (nutze GitHub Actions etc.)
  • Wenn du Reviews brauchst
  • Komplexe Build-Prozesse
  • Multi-Server Deployments

Fazit

Git Hooks sind das Original-CI/CD. Für viele Projekte reichen sie völlig aus. Kein GitHub Actions, kein Jenkins, kein Overhead. Push und fertig.

Made with by Daniel Hiller

|