Sanitized mirror from private repository - 2026-04-05 12:13:06 UTC
This commit is contained in:
309
docs/admin/PORTAINER_API_GUIDE.md
Normal file
309
docs/admin/PORTAINER_API_GUIDE.md
Normal file
@@ -0,0 +1,309 @@
|
||||
# 🐳 Portainer API Management Guide
|
||||
|
||||
*Complete guide for managing homelab infrastructure via Portainer API*
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
This guide covers how to interact with the Portainer API for managing the homelab infrastructure, including GitOps deployments, container management, and system monitoring.
|
||||
|
||||
## 🔗 API Access Information
|
||||
|
||||
### Primary Portainer Instance
|
||||
- **URL**: https://192.168.0.200:9443
|
||||
- **API Endpoint**: https://192.168.0.200:9443/api
|
||||
- **Version**: 2.39.0 (Portainer Enterprise Edition)
|
||||
- **Instance ID**: dc043e05-f486-476e-ada3-d19aaea0037d
|
||||
|
||||
### Authentication
|
||||
|
||||
Portainer supports two authentication methods:
|
||||
|
||||
**Option A — API Access Token (recommended):**
|
||||
```bash
|
||||
# Tokens starting with ptr_ use the X-API-Key header (NOT Bearer)
|
||||
export PORTAINER_TOKEN="<your-portainer-api-token>"
|
||||
curl -k -H "X-API-Key: $PORTAINER_TOKEN" https://192.168.0.200:9443/api/stacks
|
||||
```
|
||||
|
||||
**Option B — JWT (username/password):**
|
||||
```bash
|
||||
TOKEN=$(curl -k -s -X POST https://192.168.0.200:9443/api/auth \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"Username":"admin","Password":"YOUR_PASSWORD"}' | jq -r '.jwt')
|
||||
curl -k -H "Authorization: Bearer $TOKEN" https://192.168.0.200:9443/api/stacks
|
||||
```
|
||||
|
||||
> **Note:** `ptr_` API tokens must use `X-API-Key`, not `Authorization: Bearer`.
|
||||
> Using `Bearer` with a `ptr_` token returns `{"message":"Invalid JWT token"}`.
|
||||
|
||||
### Endpoint IDs
|
||||
| Endpoint | ID |
|
||||
|---|---|
|
||||
| Atlantis | 2 |
|
||||
| Calypso | 443397 |
|
||||
| Concord NUC | 443398 |
|
||||
| Homelab VM | 443399 |
|
||||
| RPi5 | 443395 |
|
||||
|
||||
## 🚀 GitOps Management
|
||||
|
||||
### Check GitOps Stack Status
|
||||
```bash
|
||||
# List all stacks with Git config
|
||||
curl -k -s -H "X-API-Key: $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/stacks | \
|
||||
jq '[.[] | select(.GitConfig.URL) | {id:.Id, name:.Name, status:.Status, file:.GitConfig.ConfigFilePath, credId:.GitConfig.Authentication.GitCredentialID}]'
|
||||
|
||||
# Get specific stack details
|
||||
curl -k -H "X-API-Key: $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/stacks/{stack_id}
|
||||
```
|
||||
|
||||
### Trigger GitOps Deployment
|
||||
```bash
|
||||
# Redeploy stack from Git (pass creds inline to bypass saved credential cache)
|
||||
curl -k -X PUT -H "X-API-Key: $PORTAINER_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"https://192.168.0.200:9443/api/stacks/{stack_id}/git/redeploy?endpointId={endpoint_id}" \
|
||||
-d '{"pullImage":true,"prune":false,"repositoryAuthentication":true,"repositoryUsername":"vish","repositoryPassword":"YOUR_GITEA_TOKEN"}'
|
||||
```
|
||||
|
||||
### Manage Git Credentials
|
||||
```bash
|
||||
# The saved Git credential used by most stacks is "portainer-homelab" (credId: 1)
|
||||
# List saved credentials:
|
||||
curl -k -s -H "X-API-Key: $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/users/1/gitcredentials | jq '.'
|
||||
|
||||
# Update the saved credential (e.g. after rotating the Gitea token):
|
||||
curl -k -s -X PUT \
|
||||
-H "X-API-Key: $PORTAINER_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"https://192.168.0.200:9443/api/users/1/gitcredentials/1" \
|
||||
-d '{"name":"portainer-homelab","username":"vish","password":"YOUR_NEW_GITEA_TOKEN"}'
|
||||
```
|
||||
|
||||
### Scan Containers for Broken Credentials
|
||||
```bash
|
||||
# Useful after a sanitization commit — finds any REDACTED values in running container envs
|
||||
python3 << 'EOF'
|
||||
import json, urllib.request, ssl
|
||||
ctx = ssl.create_default_context(); ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE
|
||||
token = "REDACTED_TOKEN"
|
||||
base = "https://192.168.0.200:9443/api"
|
||||
endpoints = {"atlantis":2,"calypso":443397,"nuc":443398,"homelab":443399,"rpi5":443395}
|
||||
def api(p):
|
||||
req = urllib.request.Request(f"{base}{p}", headers={"X-API-Key": token})
|
||||
with urllib.request.urlopen(req, context=ctx) as r: return json.loads(r.read())
|
||||
for ep_name, ep_id in endpoints.items():
|
||||
for c in api(f"/endpoints/{ep_id}/docker/containers/json?all=true"):
|
||||
info = api(f"/endpoints/{ep_id}/docker/containers/{c['Id'][:12]}/json")
|
||||
hits = [e for e in (info.get("Config",{}).get("Env") or []) if "REDACTED" in e]
|
||||
if hits: print(f"[{ep_name}] {c['Names'][0]}"); [print(f" {h}") for h in hits]
|
||||
EOF
|
||||
```
|
||||
|
||||
## 📊 Container Management
|
||||
|
||||
### List All Containers
|
||||
```bash
|
||||
# Get all containers across all endpoints
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/containers/json?all=true
|
||||
```
|
||||
|
||||
### Container Health Checks
|
||||
```bash
|
||||
# Check container status
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/containers/{container_id}/json | \
|
||||
jq '.State.Health.Status'
|
||||
|
||||
# Get container logs
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/containers/{container_id}/logs?stdout=1&stderr=1&tail=100
|
||||
```
|
||||
|
||||
## 🖥️ System Information
|
||||
|
||||
### Endpoint Status
|
||||
```bash
|
||||
# List all endpoints (servers)
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints
|
||||
|
||||
# Get system information
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/system/info
|
||||
```
|
||||
|
||||
### Resource Usage
|
||||
```bash
|
||||
# Get system stats
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/system/df
|
||||
|
||||
# Container resource usage
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/containers/{container_id}/stats?stream=false
|
||||
```
|
||||
|
||||
## 🔧 Automation Scripts
|
||||
|
||||
### Health Check Script
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# portainer-health-check.sh
|
||||
|
||||
PORTAINER_URL="https://192.168.0.200:9443"
|
||||
TOKEN="$PORTAINER_TOKEN"
|
||||
|
||||
echo "🔍 Checking Portainer API status..."
|
||||
STATUS=$(curl -k -s "$PORTAINER_URL/api/status" | jq -r '.Version')
|
||||
echo "✅ Portainer Version: $STATUS"
|
||||
|
||||
echo "🐳 Checking container health..."
|
||||
CONTAINERS=$(curl -k -s -H "Authorization: Bearer $TOKEN" \
|
||||
"$PORTAINER_URL/api/endpoints/1/docker/containers/json" | \
|
||||
jq -r '.[] | select(.State=="running") | .Names[0]' | wc -l)
|
||||
echo "✅ Running containers: $CONTAINERS"
|
||||
|
||||
echo "📊 Checking GitOps stacks..."
|
||||
STACKS=$(curl -k -s -H "Authorization: Bearer $TOKEN" \
|
||||
"$PORTAINER_URL/api/stacks" | \
|
||||
jq -r '.[] | select(.Status==1) | .Name' | wc -l)
|
||||
echo "✅ Active stacks: $STACKS"
|
||||
```
|
||||
|
||||
### GitOps Deployment Script
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deploy-stack.sh
|
||||
|
||||
STACK_NAME="$1"
|
||||
PORTAINER_URL="https://192.168.0.200:9443"
|
||||
TOKEN="$PORTAINER_TOKEN"
|
||||
|
||||
if [[ -z "$STACK_NAME" ]]; then
|
||||
echo "Usage: $0 <stack_name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🚀 Deploying stack: $STACK_NAME"
|
||||
|
||||
# Find stack ID
|
||||
STACK_ID=$(curl -k -s -H "Authorization: Bearer $TOKEN" \
|
||||
"$PORTAINER_URL/api/stacks" | \
|
||||
jq -r ".[] | select(.Name==\"$STACK_NAME\") | .Id")
|
||||
|
||||
if [[ -z "$STACK_ID" ]]; then
|
||||
echo "❌ Stack not found: $STACK_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Trigger redeploy
|
||||
curl -k -X PUT -H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$PORTAINER_URL/api/stacks/$STACK_ID/git/redeploy" \
|
||||
-d '{"RepositREDACTED_APP_PASSWORD":"main","PullImage":true}'
|
||||
|
||||
echo "✅ Deployment triggered for stack: $STACK_NAME"
|
||||
```
|
||||
|
||||
## 📈 Monitoring Integration
|
||||
|
||||
### Prometheus Metrics
|
||||
```bash
|
||||
# Get Portainer metrics (if enabled)
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/containers/json | \
|
||||
jq '[.[] | {name: .Names[0], state: .State, status: .Status}]'
|
||||
```
|
||||
|
||||
### Alerting Integration
|
||||
```bash
|
||||
# Check for unhealthy containers
|
||||
UNHEALTHY=$(curl -k -s -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/endpoints/1/docker/containers/json | \
|
||||
jq -r '.[] | select(.State != "running") | .Names[0]')
|
||||
|
||||
if [[ -n "$UNHEALTHY" ]]; then
|
||||
echo "⚠️ Unhealthy containers detected:"
|
||||
echo "$UNHEALTHY"
|
||||
fi
|
||||
```
|
||||
|
||||
## 🔐 Security Best Practices
|
||||
|
||||
### API Token Management
|
||||
- **Rotation**: Rotate API tokens regularly (monthly)
|
||||
- **Scope**: Use least-privilege tokens when possible
|
||||
- **Storage**: Store tokens securely (environment variables, secrets management)
|
||||
|
||||
### Network Security
|
||||
- **TLS**: Always use HTTPS endpoints
|
||||
- **Firewall**: Restrict API access to authorized networks
|
||||
- **Monitoring**: Log all API access for security auditing
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Authentication Failures
|
||||
```bash
|
||||
# Check token validity
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/users/me
|
||||
```
|
||||
|
||||
#### Connection Issues
|
||||
```bash
|
||||
# Test basic connectivity
|
||||
curl -k -s https://192.168.0.200:9443/api/status
|
||||
|
||||
# Check certificate issues
|
||||
openssl s_client -connect 192.168.0.200:9443 -servername atlantis.vish.local
|
||||
```
|
||||
|
||||
#### GitOps Sync Issues
|
||||
```bash
|
||||
# Check stack deployment logs
|
||||
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
|
||||
https://192.168.0.200:9443/api/stacks/{stack_id}/logs
|
||||
```
|
||||
|
||||
## 📚 API Documentation
|
||||
|
||||
### Official Resources
|
||||
- **Portainer API Docs**: https://docs.portainer.io/api/
|
||||
- **Swagger UI**: https://192.168.0.200:9443/api/docs/
|
||||
- **API Reference**: Available in Portainer web interface
|
||||
|
||||
### Useful Endpoints
|
||||
- `/api/status` - System status
|
||||
- `/api/endpoints` - Managed environments
|
||||
- `/api/stacks` - GitOps stacks
|
||||
- `/api/containers` - Container management
|
||||
- `/api/images` - Image management
|
||||
- `/api/volumes` - Volume management
|
||||
- `/api/networks` - Network management
|
||||
|
||||
## 🔄 Integration with Homelab
|
||||
|
||||
### GitOps Workflow
|
||||
1. **Code Change**: Update compose files in Git repository
|
||||
2. **Webhook**: Git webhook triggers Portainer sync (optional)
|
||||
3. **Deployment**: Portainer pulls changes and redeploys
|
||||
4. **Verification**: API checks confirm successful deployment
|
||||
|
||||
### Monitoring Integration
|
||||
- **Health Checks**: Regular API calls to verify system health
|
||||
- **Metrics Collection**: Export container metrics to Prometheus
|
||||
- **Alerting**: Trigger alerts on deployment failures or container issues
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: February 14, 2026
|
||||
**Portainer Version**: 2.33.7
|
||||
**API Version**: Compatible with Portainer EE
|
||||
**Status**: ✅ Active and Operational
|
||||
Reference in New Issue
Block a user