
Portainer
- 58 installs
- 638 repo stars
- Updated March 7, 2026
- sundial-org/awesome-openclaw-skills
Helps with ai & agent building tasks during AI-assisted development.
About
portainer is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- portainer
- AI & Agent Building
- AI-coding skill
Portainer by the numbers
- 58 all-time installs (skills.sh)
- Ranked #6,589 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sundial-org/awesome-openclaw-skills --skill portainerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 58 |
|---|---|
| repo stars | ★ 638 |
| Last updated | March 7, 2026 |
| Repository | sundial-org/awesome-openclaw-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
🐳 Portainer Skill
╔═══════════════════════════════════════════════════════════╗
║ ║
║ 🐳 P O R T A I N E R C O N T R O L C L I 🐳 ║
║ ║
║ Manage Docker containers via Portainer API ║
║ Start, stop, deploy, redeploy ║
║ ║
╚═══════════════════════════════════════════════════════════╝"Docker containers? I'll handle them from my lily pad." 🐸
---
📖 What Does This Skill Do?
The Portainer Skill gives you control over your Docker infrastructure through Portainer's REST API. Manage containers, stacks, and deployments without touching the web UI.
Features:
- 📊 Status — Check Portainer server status
- 🖥️ Endpoints — List all Docker environments
- 📦 Containers — List, start, stop, restart containers
- 📚 Stacks — List and manage Docker Compose stacks
- 🔄 Redeploy — Pull from git and redeploy stacks
- 📜 Logs — View container logs
---
⚙️ Requirements
| What | Details |
|---|---|
| Portainer | Version 2.x with API access |
| Tools | curl, jq |
| Auth | API Access Token |
Setup
1. Get API Token from Portainer:
- Log into Portainer web UI
- Click username → My Account
- Scroll to "Access tokens" → Add access token
- Copy the token (you won't see it again!)
2. Configure credentials:
# Add to ~/.clawdbot/.env
PORTAINER_URL=https://your-portainer-server:9443
PORTAINER_API_KEY=ptr_your_token_here3. Ready! 🚀
---
🛠️ Commands
status — Check Portainer Server
./portainer.sh statusOutput:
Portainer v2.27.3---
endpoints — List Environments
./portainer.sh endpointsOutput:
3: portainer (local) - ✓ online
4: production (remote) - ✓ online---
containers — List Containers
# List containers on default endpoint (4)
./portainer.sh containers
# List containers on specific endpoint
./portainer.sh containers 3Output:
steinbergerraum-web-1 running Up 2 days
cora-web-1 running Up 6 weeks
minecraft running Up 6 weeks (healthy)---
stacks — List All Stacks
./portainer.sh stacksOutput:
25: steinbergerraum - ✓ active
33: cora - ✓ active
35: minecraft - ✓ active
4: pulse-website - ✗ inactive---
stack-info — Stack Details
./portainer.sh stack-info 25Output:
{
"Id": 25,
"Name": "steinbergerraum",
"Status": 1,
"EndpointId": 4,
"GitConfig": "https://github.com/user/repo",
"UpdateDate": "2026-01-25T08:44:56Z"
}---
redeploy — Pull & Redeploy Stack 🔄
./portainer.sh redeploy 25Output:
✓ Stack 'steinbergerraum' redeployed successfullyThis will: 1. Pull latest code from git 2. Rebuild containers if needed 3. Restart the stack
---
start / stop / restart — Container Control
# Start a container
./portainer.sh start steinbergerraum-web-1
# Stop a container
./portainer.sh stop steinbergerraum-web-1
# Restart a container
./portainer.sh restart steinbergerraum-web-1
# Specify endpoint (default: 4)
./portainer.sh restart steinbergerraum-web-1 4Output:
✓ Container 'steinbergerraum-web-1' restarted---
logs — View Container Logs
# Last 100 lines (default)
./portainer.sh logs steinbergerraum-web-1
# Last 50 lines
./portainer.sh logs steinbergerraum-web-1 4 50---
🎯 Example Workflows
🚀 "Deploy Website Update"
# After merging PR
./portainer.sh redeploy 25
./portainer.sh logs steinbergerraum-web-1 4 20🔧 "Debug Container"
./portainer.sh containers
./portainer.sh logs cora-web-1
./portainer.sh restart cora-web-1📊 "System Overview"
./portainer.sh status
./portainer.sh endpoints
./portainer.sh containers
./portainer.sh stacks---
🔧 Troubleshooting
❌ "Authentication required / Repository not found"
Problem: Stack redeploy fails with git auth error
Solution: The stack needs repositoryGitCredentialID parameter. The script handles this automatically by reading from the existing stack config.
---
❌ "Container not found"
Problem: Container name doesn't match
Solution: Use exact name from ./portainer.sh containers:
- Include the full name:
steinbergerraum-web-1notsteinbergerraum - Names are case-sensitive
---
❌ "PORTAINER_URL and PORTAINER_API_KEY must be set"
Problem: Credentials not configured
Solution:
# Add to ~/.clawdbot/.env
echo "PORTAINER_URL=https://your-server:9443" >> ~/.clawdbot/.env
echo "PORTAINER_API_KEY=ptr_your_token" >> ~/.clawdbot/.env---
🔗 Integration with Clawd
"Redeploy the website"
→ ./portainer.sh redeploy 25
"Show me running containers"
→ ./portainer.sh containers
"Restart the Minecraft server"
→ ./portainer.sh restart minecraft
"What stacks do we have?"
→ ./portainer.sh stacks---
📜 Changelog
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2026-01-25 | Initial release |
---
🐸 Credits
@..@
(----)
( >__< ) "Containers are just fancy lily pads
^^ ^^ for your code to hop around!"Author: Andy Steinberger (with help from his Clawdbot Owen the Frog 🐸) Powered by: Portainer API Part of: Clawdbot Skills Collection
---
<div align="center">
Made with 💚 for the Clawdbot Community
Ribbit! 🐸
</div>
#!/bin/bash
# Portainer CLI - Control Docker containers via Portainer API
# Author: Andy Steinberger (with help from his Clawdbot Owen the Frog 🐸)
set -e
# Load config from environment or .env
PORTAINER_URL="${PORTAINER_URL:-}"
PORTAINER_API_KEY="${PORTAINER_API_KEY:-}"
# Try to load from clawdbot .env if not set
if [[ -z "$PORTAINER_URL" || -z "$PORTAINER_API_KEY" ]]; then
ENV_FILE="$HOME/.clawdbot/.env"
if [[ -f "$ENV_FILE" ]]; then
export $(grep -E "^PORTAINER_" "$ENV_FILE" | xargs)
fi
fi
if [[ -z "$PORTAINER_URL" || -z "$PORTAINER_API_KEY" ]]; then
echo "Error: PORTAINER_URL and PORTAINER_API_KEY must be set"
echo "Add to ~/.clawdbot/.env or export as environment variables"
exit 1
fi
API="$PORTAINER_URL/api"
AUTH_HEADER="X-API-Key: $PORTAINER_API_KEY"
# Helper function for API calls
api_get() {
curl -s -H "$AUTH_HEADER" "$API$1"
}
api_post() {
curl -s -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" "$API$1" -d "$2"
}
api_put() {
curl -s -X PUT -H "$AUTH_HEADER" -H "Content-Type: application/json" "$API$1" -d "$2"
}
# Commands
case "$1" in
status)
api_get "/status" | jq -r '"Portainer v\(.Version)"'
;;
endpoints|envs)
api_get "/endpoints" | jq -r '.[] | "\(.Id): \(.Name) (\(.Type == 1 | if . then "local" else "remote" end)) - \(if .Status == 1 then "✓ online" else "✗ offline" end)"'
;;
containers)
ENDPOINT="${2:-4}"
api_get "/endpoints/$ENDPOINT/docker/containers/json?all=true" | jq -r '.[] | "\(.Names[0] | ltrimstr("/"))\t\(.State)\t\(.Status)"' | column -t -s $'\t'
;;
stacks)
api_get "/stacks" | jq -r '.[] | "\(.Id): \(.Name) - \(if .Status == 1 then "✓ active" else "✗ inactive" end)"'
;;
stack-info)
STACK_ID="$2"
if [[ -z "$STACK_ID" ]]; then
echo "Usage: portainer.sh stack-info <stack-id>"
exit 1
fi
api_get "/stacks/$STACK_ID" | jq '{Id, Name, Status, EndpointId, GitConfig: .GitConfig.URL, UpdateDate: (.UpdateDate | todate)}'
;;
redeploy)
STACK_ID="$2"
if [[ -z "$STACK_ID" ]]; then
echo "Usage: portainer.sh redeploy <stack-id> [endpoint-id]"
exit 1
fi
# Get stack info for env vars and endpoint
STACK_INFO=$(api_get "/stacks/$STACK_ID")
ENDPOINT_ID=$(echo "$STACK_INFO" | jq -r '.EndpointId')
ENV_VARS=$(echo "$STACK_INFO" | jq -c '.Env')
GIT_CRED_ID=$(echo "$STACK_INFO" | jq -r '.GitConfig.Authentication.GitCredentialID // 0')
PAYLOAD=$(jq -n \
--argjson env "$ENV_VARS" \
--argjson gitCredId "$GIT_CRED_ID" \
'{env: $env, prune: false, pullImage: true, repositoryAuthentication: true, repositoryGitCredentialID: $gitCredId}')
RESULT=$(api_put "/stacks/$STACK_ID/git/redeploy?endpointId=$ENDPOINT_ID" "$PAYLOAD")
if echo "$RESULT" | jq -e '.Id' > /dev/null 2>&1; then
STACK_NAME=$(echo "$RESULT" | jq -r '.Name')
echo "✓ Stack '$STACK_NAME' redeployed successfully"
else
echo "✗ Redeploy failed:"
echo "$RESULT" | jq -r '.message // .details // .'
exit 1
fi
;;
start)
ENDPOINT="${3:-4}"
CONTAINER="$2"
if [[ -z "$CONTAINER" ]]; then
echo "Usage: portainer.sh start <container-name> [endpoint-id]"
exit 1
fi
# Get container ID
CONTAINER_ID=$(api_get "/endpoints/$ENDPOINT/docker/containers/json?all=true" | jq -r ".[] | select(.Names[0] == \"/$CONTAINER\") | .Id")
if [[ -z "$CONTAINER_ID" ]]; then
echo "✗ Container '$CONTAINER' not found"
exit 1
fi
api_post "/endpoints/$ENDPOINT/docker/containers/$CONTAINER_ID/start" "{}" > /dev/null
echo "✓ Container '$CONTAINER' started"
;;
stop)
ENDPOINT="${3:-4}"
CONTAINER="$2"
if [[ -z "$CONTAINER" ]]; then
echo "Usage: portainer.sh stop <container-name> [endpoint-id]"
exit 1
fi
CONTAINER_ID=$(api_get "/endpoints/$ENDPOINT/docker/containers/json?all=true" | jq -r ".[] | select(.Names[0] == \"/$CONTAINER\") | .Id")
if [[ -z "$CONTAINER_ID" ]]; then
echo "✗ Container '$CONTAINER' not found"
exit 1
fi
api_post "/endpoints/$ENDPOINT/docker/containers/$CONTAINER_ID/stop" "{}" > /dev/null
echo "✓ Container '$CONTAINER' stopped"
;;
restart)
ENDPOINT="${3:-4}"
CONTAINER="$2"
if [[ -z "$CONTAINER" ]]; then
echo "Usage: portainer.sh restart <container-name> [endpoint-id]"
exit 1
fi
CONTAINER_ID=$(api_get "/endpoints/$ENDPOINT/docker/containers/json?all=true" | jq -r ".[] | select(.Names[0] == \"/$CONTAINER\") | .Id")
if [[ -z "$CONTAINER_ID" ]]; then
echo "✗ Container '$CONTAINER' not found"
exit 1
fi
api_post "/endpoints/$ENDPOINT/docker/containers/$CONTAINER_ID/restart" "{}" > /dev/null
echo "✓ Container '$CONTAINER' restarted"
;;
logs)
ENDPOINT="${3:-4}"
CONTAINER="$2"
TAIL="${4:-100}"
if [[ -z "$CONTAINER" ]]; then
echo "Usage: portainer.sh logs <container-name> [endpoint-id] [tail-lines]"
exit 1
fi
CONTAINER_ID=$(api_get "/endpoints/$ENDPOINT/docker/containers/json?all=true" | jq -r ".[] | select(.Names[0] == \"/$CONTAINER\") | .Id")
if [[ -z "$CONTAINER_ID" ]]; then
echo "✗ Container '$CONTAINER' not found"
exit 1
fi
curl -s -H "$AUTH_HEADER" "$API/endpoints/$ENDPOINT/docker/containers/$CONTAINER_ID/logs?stdout=true&stderr=true&tail=$TAIL" | strings
;;
*)
echo "Portainer CLI - Control Docker via Portainer API"
echo ""
echo "Usage: portainer.sh <command> [args]"
echo ""
echo "Commands:"
echo " status Show Portainer version"
echo " endpoints List all environments"
echo " containers [endpoint] List containers (default endpoint: 4)"
echo " stacks List all stacks"
echo " stack-info <id> Show stack details"
echo " redeploy <stack-id> Pull and redeploy a stack"
echo " start <container> Start a container"
echo " stop <container> Stop a container"
echo " restart <container> Restart a container"
echo " logs <container> [ep] [n] Show container logs (last n lines)"
echo ""
echo "Environment:"
echo " PORTAINER_URL Portainer server URL"
echo " PORTAINER_API_KEY API access token"
;;
esac