GitHub Actions Deployment Workflow¶
This template automates deployment to the EgyGeeks server using GitHub Actions with a self-hosted runner.
How It Works¶
- Push code to GitHub (main branch)
- GitHub Actions runner on server detects push
- Pulls latest code
- Stops old containers
- Builds fresh Docker image
- Starts new containers
- Shows deployment status
Template¶
name: Deploy to EgyGeeks Server
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: [self-hosted]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up environment
run: |
echo "Deploying MyApp"
echo "Commit: ${{ github.sha }}"
echo "Branch: ${{ github.ref_name }}"
- name: Stop old containers
run: |
docker compose -f docker-compose.yml down || true
- name: Build Docker image
run: |
docker compose -f docker-compose.yml build --no-cache
- name: Start new containers
run: |
docker compose -f docker-compose.yml up -d
- name: Wait for services to be healthy
run: |
echo "Waiting for services to start..."
sleep 5
- name: Cleanup old images
run: |
docker image prune -f
- name: Deployment summary
run: |
echo "✅ Deployment completed successfully!"
echo "📚 App URL: https://app.egygeeks.com"
echo "🐳 Containers:"
docker compose ps
echo ""
echo "Container status:"
docker ps --filter "name=my-app" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
Setup Steps¶
1. Create Workflow File¶
In your project repository:
2. Customize¶
Replace these values:
MyApp→ Your application namehttps://app.egygeeks.com→ Your domainmy-app→ Your container name (matches docker-compose.yml)
3. Commit and Push¶
4. Monitor Deployment¶
View workflow runs in GitHub:
Configuration Options¶
Trigger on Specific Branches¶
Deploy from multiple branches:
Manual Deployment Only¶
Remove automatic deployment on push:
Deploy on Tag¶
Deploy when creating a release:
Add Environment Variables¶
Pass secrets to the build:
- name: Build Docker image
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
docker compose build --no-cache
Add secrets in GitHub:
Understanding the Steps¶
Checkout Code¶
Pulls your latest code from GitHub to the server.
Stop Old Containers¶
|| trueprevents errors if containers don't exist- Gracefully stops and removes old containers
Build Docker Image¶
Why --no-cache?
Without --no-cache, Docker may use cached layers and not pick up your latest code changes. Always use --no-cache in CI/CD.
Start New Containers¶
-druns in detached mode (background)- Starts containers defined in docker-compose.yml
Cleanup Old Images¶
Removes dangling images to save disk space.
Advanced Examples¶
Multi-Environment Deployment¶
name: Deploy
on:
push:
branches: [main, staging]
jobs:
deploy:
runs-on: [self-hosted]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set environment
id: env
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "environment=production" >> $GITHUB_OUTPUT
echo "domain=app.egygeeks.com" >> $GITHUB_OUTPUT
else
echo "environment=staging" >> $GITHUB_OUTPUT
echo "domain=staging.egygeeks.com" >> $GITHUB_OUTPUT
fi
- name: Deploy
run: |
docker compose -f docker-compose.${{ steps.env.outputs.environment }}.yml down || true
docker compose -f docker-compose.${{ steps.env.outputs.environment }}.yml up -d --build
Run Tests Before Deploy¶
jobs:
test:
runs-on: [self-hosted]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run tests
run: |
docker compose -f docker-compose.test.yml up --abort-on-container-exit
docker compose -f docker-compose.test.yml down
deploy:
needs: test # Only deploy if tests pass
runs-on: [self-hosted]
steps:
# ... deployment steps
Slack Notifications¶
- name: Notify Slack
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Deployment to production'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Troubleshooting¶
Workflow not triggering¶
Check runner status:
Verify runner is online:
Build fails with "permission denied"¶
Runner user needs Docker permissions:
Containers not starting¶
Check logs in GitHub Actions:
Check container logs on server:
"docker compose: command not found"¶
Use modern Docker Compose (v2):
Best Practices¶
1. Always Use --no-cache¶
Prevents stale builds:
2. Clean Up Resources¶
Prevent disk space issues:
3. Health Check Wait¶
Give containers time to become healthy:
4. Rollback on Failure¶
- name: Deploy
id: deploy
run: |
docker compose up -d
- name: Rollback on failure
if: failure()
run: |
docker compose down
docker compose -f docker-compose.backup.yml up -d