| name | portainer-deploy |
| description | Setup automated deployment with GitHub Actions, ghcr.io, and Portainer webhooks. Supports Traefik reverse proxy and multi-service Docker Compose. Use when user wants to setup CI/CD, deployment, or Portainer integration. |
Portainer Deploy Skill
This skill helps set up automated deployment pipeline with:
- GitHub Container Registry (ghcr.io)
- GitHub Actions for CI/CD
- Portainer webhooks for automatic redeployment
- Traefik reverse proxy support
- Multi-service Docker Compose support
When to Use This Skill
Use this skill when the user wants to:
- Set up automated deployment for their project
- Configure CI/CD with GitHub Actions
- Integrate with Portainer for container management
- Add Traefik reverse proxy with automatic HTTPS
- Deploy multi-service applications
Deployment Process
1. Analyze Project Structure
First, understand the current project setup:
# Check for existing files
ls -la Dockerfile docker-compose.yml .github/workflows/
Determine:
- Is there a Dockerfile? (if not, this skill cannot help - Dockerfile is required)
- Is there a docker-compose.yml? (analyze or create)
- Are there existing GitHub Actions workflows?
- How many services does the project need? (single or multi-service)
- What is the main branch name? (main or master)
2. Interactive Questions
If uncertain, ask the user:
- Main branch: "What is your main branch name? (main/master)"
- Services: "Is this a single service or multi-service application?"
- Traefik: "Do you want to use Traefik reverse proxy with automatic HTTPS? (yes/no)"
- Domain: If Traefik - "What domain will you use? (can be configured via Portainer env vars)"
- Network: If Traefik - "What is your Traefik network name? (can be configured via Portainer env vars)"
- Webhooks: "How many Portainer stacks will you deploy? (usually 1, or 2+ for multi-stack)"
Note: Domain and network name can be provided via Portainer environment variables for flexibility across environments.
3. Generate or Update GitHub Actions Workflow
Create .github/workflows/deploy.yml:
Key elements:
- Trigger on push to main branch + workflow_dispatch
- Login to ghcr.io using GITHUB_TOKEN
- Build and push Docker image with SHA tag ONLY (NOT latest):
${{ github.sha }} - Checkout or create
deploybranch usinggit checkout -B deploy - Update docker-compose.yml with SHA tag using sed
- Commit changes to
deploybranch - Force push to
deploybranch with--force - Trigger Portainer webhook(s)
CRITICAL: Never use :latest tag in production deployments. Always use specific SHA tags for traceability.
Template reference: See templates/github-workflow.yml
Important notes:
- Use
ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}for image tag - The main branch keeps
:latestas a placeholder (will be replaced in deploy branch) - Portainer should watch the
deploybranch, NOT the main branch - Update ALL docker-compose files if multiple exist (e.g., main + bot-hoster)
- Add multiple webhook steps if deploying to multiple Portainer stacks
- Use
--forcewhen pushing to deploy branch (it's a CI-only branch)
4. Generate or Update docker-compose.yml
Important:
- Always use the actual repository path from
git remote get-url origininstead of placeholder text - In the main branch, use
:latestas a placeholder (it will be replaced in the deploy branch) - In the deploy branch, the image tag will be the specific SHA (updated by CI/CD)
For single service with Traefik (main branch):
version: "3.8"
services:
app:
# The image tag will be replaced by GitHub Actions workflow in the deploy branch
image: ghcr.io/owner/repo:latest # Use actual owner/repo from git remote
container_name: app-name
networks:
- traefik_network
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`${HOSTNAME}`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=myresolver"
- "traefik.http.services.app.loadbalancer.server.port=8080"
volumes:
- ./data:/app/data
restart: unless-stopped
environment:
- ENV_VAR=${ENV_VAR}
networks:
traefik_network:
name: ${NETWORK_NAME:-traefik_network} # Allow network name override via env var
external: true
Note: The deploy branch will have the same structure but with image: ghcr.io/owner/repo:abc123... (full SHA)
For multi-service:
- Include all services (e.g., app + database + cache)
- Set up service dependencies with
depends_on - Use same image for multiple services with different
commandif needed - Configure internal networks for inter-service communication
Template reference: See templates/docker-compose-traefik.yml and templates/docker-compose-simple.yml
5. Update Strategy
If files exist:
GitHub Actions workflow exists:
- Check if it's similar to our pattern (build → push → update compose → webhook)
- If similar: Update in place (fix image tags, add missing webhooks, etc.)
- If different: Ask user if they want to replace or keep existing
docker-compose.yml exists:
- Check if it has correct image format (ghcr.io/owner/repo:latest)
- Check if Traefik labels are present (if user wants Traefik)
- Update image tags to use ghcr.io format
- Add Traefik labels if requested
- Preserve existing environment variables and volumes
If files don't exist:
- Create new files from templates
- Customize based on project structure
- Get actual repository owner/repo from:
git remote get-url origin - Use actual values in docker-compose.yml image field
6. Remind About Secrets, Environment Variables, and Deploy Branch
After generating files, remind the user:
Important Setup Steps:
1. Add GitHub Secret:
- Go to: https://github.com/OWNER/REPO/settings/secrets/actions
- Click "New repository secret"
- Name: PORTAINER_REDEPLOY_HOOK
- Value: Your Portainer webhook URL (get from Portainer stack settings)
If you have multiple stacks, add:
- PORTAINER_REDEPLOY_HOOK_2 (for second stack)
- PORTAINER_REDEPLOY_HOOK_BOT (for bot-hoster, etc.)
2. Configure Portainer Stack:
- CRITICAL: Use the 'deploy' branch, NOT the main branch
- The deploy branch contains SHA-tagged images (e.g., ghcr.io/owner/repo:abc123...)
- This ensures you know exactly which version is deployed
Portainer Environment Variables to Configure:
- HOSTNAME: Your domain name (e.g., bot.example.com)
- NETWORK_NAME: Traefik network name (optional, defaults to traefik_network)
- Plus any application-specific environment variables
3. To get webhook URL from Portainer:
- Open your stack in Portainer
- Click on "Webhooks"
- Copy the webhook URL
How it works:
- Push to main branch → CI builds and tags image with commit SHA
- CI updates deploy branch with the new SHA tag
- Portainer watches deploy branch and auto-updates
- You always know which exact commit is deployed
Project Examples
Refer to these projects for patterns:
- virusgame - Multi-service with WebSocket support
- madrookbot - Multi-service (bot + qdrant + tool-api)
- countrycounter - Single service with Traefik
Troubleshooting
Image tags not updating:
- Check sed patterns in GitHub Actions workflow
- Verify all docker-compose files are included in sed commands
Webhook not triggering:
- Verify secret name matches in workflow and GitHub settings
- Check Portainer webhook URL is correct
- Test webhook manually with curl
Traefik not routing:
- Verify network exists:
docker network ls - Check Traefik labels syntax
- Ensure HOSTNAME environment variable is set
Implementation Checklist
After running this skill, verify:
-
.github/workflows/deploy.ymlcreated/updated -
docker-compose.ymlhas correct image format - Traefik labels added (if requested)
- User reminded about GitHub secrets
- All services defined in compose file
- Environment variables documented
- Volumes configured for persistence
Next Steps for User
After setup:
- Add PORTAINER_REDEPLOY_HOOK to GitHub secrets
- Set up Portainer stack using the docker-compose.yml FROM THE DEPLOY BRANCH
- Configure environment variables in Portainer
- Test deployment: Push to main branch
- Verify CI creates/updates deploy branch with SHA tag
- Verify Portainer auto-updates the stack