309 lines
9.6 KiB
Markdown
309 lines
9.6 KiB
Markdown
# 🐳 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="REDACTED_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 |