# 🐳 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 " 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