Diese Beispiele zeigen, wie Sie GitHub Actions verwenden können, um Ihre Anwendung automatisch mit HostKit auf Ihrem VPS zu deployen.
- Basis-Workflow
- Multi-Stage Deployment
- Docker Build mit Caching
- Blue-Green Deployment
- Rollback bei Fehlern
- Secrets Konfiguration
Einfacher Workflow für automatisches Deployment bei Push auf main branch.
name: Deploy to VPS
on:
push:
branches: [main]
workflow_dispatch: # Erlaubt manuelles Triggern
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker Image
run: |
docker build -t ${{ secrets.DOMAIN }} .
docker save ${{ secrets.DOMAIN }} > image.tar
- name: Upload Image to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
source: "image.tar"
target: "/opt/domains/${{ secrets.DOMAIN }}/deploy/"
- name: Deploy on VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
script: |
sudo hostkit deploy ${{ secrets.DOMAIN }} /opt/domains/${{ secrets.DOMAIN }}/deploy/image.tar
rm -f /opt/domains/${{ secrets.DOMAIN }}/deploy/image.tar
- name: Health Check
run: |
sleep 10
curl -f https://${{ secrets.DOMAIN }} || exit 1Deployment für verschiedene Umgebungen (Staging & Production).
name: Multi-Stage Deployment
on:
push:
branches:
- develop # Staging
- main # Production
workflow_dispatch:
inputs:
environment:
description: "Environment to deploy"
required: true
type: choice
options:
- staging
- production
jobs:
determine-environment:
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.set-env.outputs.environment }}
domain: ${{ steps.set-env.outputs.domain }}
steps:
- name: Determine Environment
id: set-env
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
elif [ "${{ github.ref }}" == "refs/heads/main" ]; then
echo "environment=production" >> $GITHUB_OUTPUT
else
echo "environment=staging" >> $GITHUB_OUTPUT
fi
if [ "$(cat $GITHUB_OUTPUT | grep environment | cut -d= -f2)" == "production" ]; then
echo "domain=${{ secrets.PRODUCTION_DOMAIN }}" >> $GITHUB_OUTPUT
else
echo "domain=${{ secrets.STAGING_DOMAIN }}" >> $GITHUB_OUTPUT
fi
build:
needs: determine-environment
runs-on: ubuntu-latest
environment: ${{ needs.determine-environment.outputs.environment }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker Image
run: |
docker build \
--build-arg ENVIRONMENT=${{ needs.determine-environment.outputs.environment }} \
-t ${{ needs.determine-environment.outputs.domain }} .
docker save ${{ needs.determine-environment.outputs.domain }} > image.tar
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: docker-image
path: image.tar
retention-days: 1
deploy:
needs: [determine-environment, build]
runs-on: ubuntu-latest
environment: ${{ needs.determine-environment.outputs.environment }}
steps:
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: docker-image
- name: Upload to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
source: "image.tar"
target: "/opt/domains/${{ needs.determine-environment.outputs.domain }}/deploy/"
- name: Deploy on VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
script: |
sudo hostkit deploy ${{ needs.determine-environment.outputs.domain }} \
/opt/domains/${{ needs.determine-environment.outputs.domain }}/deploy/image.tar
rm -f /opt/domains/${{ needs.determine-environment.outputs.domain }}/deploy/image.tar
- name: Verify Deployment
run: |
sleep 15
for i in {1..5}; do
if curl -f https://${{ needs.determine-environment.outputs.domain }}; then
echo "Deployment successful!"
exit 0
fi
echo "Attempt $i failed, retrying..."
sleep 10
done
exit 1Optimierter Workflow mit Docker Layer Caching für schnellere Builds.
name: Deploy with Docker Cache
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker Layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build Docker Image
uses: docker/build-push-action@v5
with:
context: .
push: false
outputs: type=docker,dest=/tmp/image.tar
tags: ${{ secrets.DOMAIN }}:latest
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
- name: Move Cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
- name: Load and Save Image
run: |
docker load -i /tmp/image.tar
docker save ${{ secrets.DOMAIN }}:latest > image.tar
- name: Upload Image to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
source: "image.tar"
target: "/opt/domains/${{ secrets.DOMAIN }}/deploy/"
- name: Deploy on VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
script: |
sudo hostkit deploy ${{ secrets.DOMAIN }} /opt/domains/${{ secrets.DOMAIN }}/deploy/image.tar
rm -f /opt/domains/${{ secrets.DOMAIN }}/deploy/image.tarZero-Downtime Deployment mit automatischem Rollback bei Fehlern.
name: Blue-Green Deployment
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Build Docker Image
run: |
docker build -t ${{ secrets.DOMAIN }}:${{ github.sha }} .
docker save ${{ secrets.DOMAIN }}:${{ github.sha }} > image.tar
- name: Upload Image to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
source: "image.tar"
target: "/opt/domains/${{ secrets.DOMAIN }}/deploy/"
- name: Deploy New Version
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
script: |
# Save current version for rollback
CURRENT_VERSION=$(sudo hostkit versions ${{ secrets.DOMAIN }} | grep "current" | awk '{print $1}')
echo "Current version: $CURRENT_VERSION"
# Deploy new version
sudo hostkit deploy ${{ secrets.DOMAIN }} /opt/domains/${{ secrets.DOMAIN }}/deploy/image.tar
# Wait for container to be ready
sleep 10
# Health check
if curl -f -s --max-time 10 https://${{ secrets.DOMAIN }}/health > /dev/null; then
echo "Health check passed!"
rm -f /opt/domains/${{ secrets.DOMAIN }}/deploy/image.tar
else
echo "Health check failed! Rolling back..."
sudo hostkit switch ${{ secrets.DOMAIN }} $CURRENT_VERSION
exit 1
fi
- name: Notify Success
if: success()
run: |
echo "✅ Deployment successful: ${{ github.sha }}"
- name: Notify Failure
if: failure()
run: |
echo "❌ Deployment failed and was rolled back"
exit 1Separater Workflow für manuelles Rollback auf vorherige Version.
name: Rollback Deployment
on:
workflow_dispatch:
inputs:
version:
description: "Version to rollback to (leave empty for previous)"
required: false
type: string
jobs:
rollback:
runs-on: ubuntu-latest
steps:
- name: Get Available Versions
uses: appleboy/ssh-action@v1.0.3
id: versions
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
script: |
sudo hostkit versions ${{ secrets.DOMAIN }}
- name: Rollback to Version
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
script: |
if [ -n "${{ inputs.version }}" ]; then
echo "Rolling back to version: ${{ inputs.version }}"
sudo hostkit switch ${{ secrets.DOMAIN }} ${{ inputs.version }}
else
echo "Rolling back to previous version"
VERSIONS=$(sudo hostkit versions ${{ secrets.DOMAIN }} | grep -v "current" | head -2 | tail -1 | awk '{print $1}')
sudo hostkit switch ${{ secrets.DOMAIN }} $VERSIONS
fi
- name: Verify Rollback
run: |
sleep 10
curl -f https://${{ secrets.DOMAIN }} || exit 1
echo "✅ Rollback successful!"
- name: Notify
run: |
echo "Rolled back to version: ${{ inputs.version || 'previous' }}"Navigieren Sie zu: Settings → Secrets and variables → Actions → New repository secret
| Secret Name | Beschreibung | Beispiel |
|---|---|---|
VPS_HOST |
IP-Adresse oder Domain des VPS | 192.168.1.100 oder vps.example.com |
DEPLOY_USER |
SSH-Benutzername für Deployment | deploy-example-com |
DEPLOY_SSH_KEY |
Private SSH-Key (RSA oder Ed25519) | Inhalt von hostkit show-key |
VPS_PORT |
SSH-Port (optional) | 22 (Standard) |
DOMAIN |
Domain der Website | example.com |
STAGING_DOMAIN |
Staging-Domain (optional) | staging.example.com |
PRODUCTION_DOMAIN |
Production-Domain (optional) | example.com |
-
SSH-Key auf VPS erstellen:
sudo hostkit add-key example.com github-actions
-
Private Key anzeigen:
sudo hostkit show-key example.com github-actions
-
Key als GitHub Secret hinzufügen:
- Kopieren Sie den Private Key Inhalt
- Fügen Sie ihn als
DEPLOY_SSH_KEYSecret hinzu
Für verschiedene Umgebungen können Sie auch Environment Secrets verwenden:
Settings → Environments → New environment
Erstellen Sie Environments wie staging und production mit eigenen Secrets:
VPS_HOSTDEPLOY_USERDEPLOY_SSH_KEY
Fügen Sie Slack/Discord-Benachrichtigungen hinzu:
- name: Notify Slack
if: always()
uses: slackapi/slack-github-action@v1.25.0
with:
payload: |
{
"text": "Deployment ${{ job.status }}: ${{ github.repository }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Deployment *${{ job.status }}* for `${{ secrets.DOMAIN }}`\n*Commit:* ${{ github.sha }}\n*Author:* ${{ github.actor }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}Integrieren Sie Security Scans vor dem Deployment:
- name: Run Trivy Vulnerability Scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ secrets.DOMAIN }}:latest
format: "sarif"
output: "trivy-results.sarif"
- name: Upload Trivy Results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "trivy-results.sarif"Fügen Sie manuelle Approval für Production hinzu:
deploy-production:
needs: build
runs-on: ubuntu-latest
environment:
name: production
url: https://${{ secrets.PRODUCTION_DOMAIN }}
steps:
# ... deployment stepsKonfigurieren Sie in Settings → Environments → production:
- Required reviewers
- Wait timer
- Deployment branches
Führen Sie Tests vor dem Deployment durch:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Tests
run: |
docker build -t test-image .
docker run test-image npm test
deploy:
needs: test
runs-on: ubuntu-latest
# ... deployment stepsLösung: Überprüfen Sie SSH-Key und Berechtigungen:
sudo hostkit show-key example.com github-actions
sudo hostkit list-keys example.comLösung: Prüfen Sie Logs:
sudo hostkit logs example.com 100Lösung: Erhöhen Sie Timeouts und fügen Sie Retry-Logik hinzu:
- name: Deploy with Retry
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: |
# deployment commandFür optimale Kompatibilität mit HostKit:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD node -e "require('http').get('http://localhost:3000/health', (res) => process.exit(res.statusCode === 200 ? 0 : 1))"
CMD ["node", "dist/index.js"]Wichtig:
- Der Container muss auf einem Port lauschen (z.B. 3000)
- Health-Check-Endpoint implementieren
- Proper Signal-Handling für graceful shutdown