Files
homelab-optimized/docs/admin/PORTAINER_API_GUIDE.md
Gitea Mirror Bot ff53451ebe
Some checks failed
Documentation / Deploy to GitHub Pages (push) Has been cancelled
Documentation / Build Docusaurus (push) Has been cancelled
Sanitized mirror from private repository - 2026-04-19 08:32:25 UTC
2026-04-19 08:32:25 +00:00

9.6 KiB

🐳 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

Authentication

Portainer supports two authentication methods:

Option A — API Access Token (recommended):

# 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):

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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

#!/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

#!/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

# 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

# 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

# Check token validity
curl -k -H "Authorization: Bearer $PORTAINER_TOKEN" \
  https://192.168.0.200:9443/api/users/me

Connection Issues

# 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

# 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

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