- Add Dockerfile for portainer-automation (multi-stage build) - Add standalone docker-compose.yaml for PA service - Add application-prod.properties (prod Portainer :9443) - Add application-ci.properties (CI test endpoints) - Add .env.example with documented env vars for PA - Add PORTAINER_STACK.md with deployment guide, env var tables, architecture diagram - Add CI workflow (portainer_automation_build_push.yml) for image build/push - Update root docker-compose.yaml to include PA service
8.5 KiB
Portainer Automation Service — Portainer Stack Guide
Overview
The Portainer Automation service exposes a single endpoint (POST /api/deploy/{stackId})
that triggers a redeploy of a Portainer stack. CI workflows call this endpoint after
pushing new container images.
The service is deployed as a separate Portainer stack (Option B) — independent from the CFTunnels stacks — so it can be reused by other projects.
Architecture Diagram
┌─────────────────┐ push tag ┌──────────────────┐
│ Gitea Runner │ ──────────────► │ Docker Registry │
│ (CI workflow) │ │ :8928 │
└────────┬────────┘ └──────────────────┘
│
│ POST /api/deploy/{stackId}
│ X-API-Key: <service-api-key>
▼
┌─────────────────────────────────────┐
│ Portainer Automation Service │
│ (portainer-automation_${ENV}) │
│ port: 8081 │
└──────────┬──────────────────────────┘
│ PUT /api/stacks/{id}/git/redeploy
│ X-API-Key: <portainer-api-key>
▼
┌─────────────────────────────────────┐
│ Portainer CE │
│ Dev: :9442 / Prod: :9443 │
└──────────┬──────────────────────────┘
│ git pull + stack redeploy
▼
┌─────────────────────────────────────┐
│ Target Stack (e.g. CFTunnels) │
│ Docker Compose from Git │
└─────────────────────────────────────┘
Portainer Stack Configuration
Create a New Stack in Portainer
- Navigate: Portainer UI → Stacks → Add stack
- Name:
portainer-automation-{env}(e.g.portainer-automation-dev) - Build method: Git repository
- Repository URL:
https://gitea.hithomelabs.com/Hithomelabs/CFTunnels.git - Repository reference:
refs/heads/test(dev) orrefs/heads/main(prod) - Compose path:
portainer-automation/docker-compose.yaml - Environment variables: See table below
Environment Variables — Dev
| Variable | Value | Description |
|---|---|---|
ENV |
dev |
Environment label |
HOST_PORT |
8081 |
Host port mapping |
SPRING_PROFILES_ACTIVE |
local |
Spring profile (uses devdocker.hithomelabs.com) |
PORTAINER_API_KEY |
ptk_xxxx_dev_xxxx |
Portainer Dev API access token |
PORTAINER_SERVICE_API_KEY |
(generate a random key) | CI → Service auth key |
Portainer Dev endpoint: https://devdocker.hithomelabs.com (port 9442 behind Cloudflare Tunnel)
Environment Variables — Prod
| Variable | Value | Description |
|---|---|---|
ENV |
prod |
Environment label |
HOST_PORT |
8082 |
Host port mapping |
SPRING_PROFILES_ACTIVE |
prod |
Spring profile (uses 192.168.0.100:9443) |
PORTAINER_API_KEY |
ptk_xxxx_prod_xxxx |
Portainer Prod API access token |
PORTAINER_SERVICE_API_KEY |
(generate a different random key) | CI → Service auth key |
Portainer Prod endpoint: https://192.168.0.100:9443 (internal network)
Deployment Steps
Prerequisites
- Portainer Automation Docker image is built and pushed (see CI workflow)
- Portainer API access tokens created (Portainer UI → My access tokens)
- Gitea secrets configured:
PORTAINER_SERVICE_API_KEY— shared secret for devPORTAINER_SERVICE_API_KEY_PROD— shared secret for prod
Step 1: Build and Push Image
The CI workflow .gitea/workflows/portainer_automation_build_push.yml handles this
automatically on push to the test branch.
To build manually:
./gradlew :portainer-automation:bootBuildImage \
--imageName=192.168.0.100:8928/hithomelabs/portainer-automation:<version>
Step 2: Create Stack in Portainer (Dev)
- Log in to Portainer Dev (
https://devdocker.hithomelabs.com) - Stacks → Add stack
- Name:
portainer-automation-dev - Build method: Git repository
- Repository:
https://gitea.hithomelabs.com/Hithomelabs/CFTunnels.git - Reference:
refs/heads/test - Compose path:
portainer-automation/docker-compose.yaml - Add env vars from the Dev table above
- Click Deploy the stack
Step 3: Verify Dev Deployment
# Redeploy a stack via the automation service
curl -X POST \
-H "X-API-Key: <service-api-key>" \
http://portainer-automation-dev:8081/api/deploy/<stack-id>
# Expected: "Deployment initiated for stack <stack-id>"
Step 4: Create Stack in Portainer (Prod)
- Log in to Portainer Prod (
https://192.168.0.100:9443) - Follow the same steps as Step 2, with:
- Name:
portainer-automation-prod - Reference:
refs/heads/main - Env vars from the Prod table above
- Name:
Step 5: Wire CI Triggers
Ensure CI workflows call the automation endpoint:
Dev (in test_image_build_push.yml):
- name: Trigger Portainer Redeploy (Dev)
if: success()
env:
PORTAINER_AUTOMATION_URL: "http://portainer-automation-dev:8081"
STACK_ID: ${{ vars.PORTAINER_AUTOMATION_DEV_STACK_ID }}
API_KEY: ${{ secrets.PORTAINER_SERVICE_API_KEY }}
run: |
wget -q --no-check-certificate --timeout=30 \
--header="X-API-Key: $API_KEY" \
-O - \
"$PORTAINER_AUTOMATION_URL/api/deploy/$STACK_ID" 2>&1 \
|| echo "Deploy trigger failed (non-fatal)"
Prod (in prod_image_tag_promote.yaml):
- name: Trigger Portainer Redeploy (Prod)
if: success()
env:
PORTAINER_AUTOMATION_URL: "http://portainer-automation-prod:8082"
STACK_ID: ${{ vars.PORTAINER_AUTOMATION_PROD_STACK_ID }}
API_KEY: ${{ secrets.PORTAINER_SERVICE_API_KEY_PROD }}
run: |
wget -q --no-check-certificate --timeout=30 \
--header="X-API-Key: $API_KEY" \
-O - \
"$PORTAINER_AUTOMATION_URL/api/deploy/$STACK_ID" 2>&1 \
|| echo "Deploy trigger failed (non-fatal)"
Configuration Reference
Profile Resolution Order
The service resolves configuration in this order (later overrides earlier):
application.properties— default:https://192.168.0.100:9442application-{profile}.properties— profile-specific overrides- Environment variables — highest priority
Profile Purposes
| Profile | File | Portainer Target | Use Case |
|---|---|---|---|
| default | application.properties |
192.168.0.100:9442 (Dev) |
CI/CD, Docker compose |
local |
application-local.properties |
devdocker.hithomelabs.com |
Local dev via Cloudflare Tunnel |
ci |
application-ci.properties |
portainer-test:9000 |
CI pipeline testing |
prod |
application-prod.properties |
192.168.0.100:9443 (Prod) |
Production deployment |
Environment Variable Reference
| Variable | Required | Default | Description |
|---|---|---|---|
SPRING_PROFILES_ACTIVE |
No | (none) | Active Spring profile |
PORTAINER_API_KEY |
Yes | — | Portainer API access token |
PORTAINER_SERVICE_API_KEY |
Yes | — | Shared secret for CI → service auth |
ENV |
No | test |
Deployment environment label |
HOST_PORT |
No | 8081 |
External port mapping |
Health Checks
The service exposes no dedicated health endpoint yet. Verify availability by:
# Check that the service responds (expects 401 Unauthorized without API key)
curl -X POST http://portainer-automation-dev:8081/api/deploy/1
# Expected: {"timestamp":"...","status":401,"error":"Unauthorized",...}
# A 401 response means the service is running.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
Connection refused |
Service not running | Check stack status in Portainer |
Invalid API key |
Wrong PORTAINER_SERVICE_API_KEY |
Verify env var matches CI secret |
Portainer error: Unauthorized |
Wrong PORTAINER_API_KEY |
Regenerate token in Portainer UI |
Stack not found |
Wrong stack ID | Verify stack ID in Portainer URL |