Files
portainer-core/docs/AUTOMATION.md
T

344 lines
7.8 KiB
Markdown

# Stack Automation Guide
## Overview
The `update-stack.sh` script enables programmatic stack updates via Portainer's REST API. This allows LLM agents (like Claude) and automation scripts to safely update Portainer stacks without requiring UI access.
## Quick Start
```bash
# Navigate to stacks directory
cd /home/jpmschweitzer/Projects/portainer-core/stacks
# Update a stack (interactive mode - first time)
./update-stack.sh open-webui.yml
# Subsequent updates (uses stored token)
./update-stack.sh open-webui.yml
```
## How It Works
### Authentication Flow
1. **First Run:**
- Prompts for Portainer username/password
- Authenticates with Portainer API
- Generates JWT access token
- Saves token to `.portainer-token` (gitignored)
2. **Subsequent Runs:**
- Reads token from `.portainer-token`
- Uses token for API calls
- No credential prompts needed
### Update Process
1. Reads YAML file from `stacks/` directory
2. Authenticates with Portainer (or uses cached token)
3. Looks up stack by name (filename without .yml)
4. Sends updated stack configuration via API
5. Portainer validates and applies changes
## Usage Modes
### Interactive Mode (Human Operators)
```bash
./update-stack.sh open-webui.yml
```
**First run prompts for:**
- Portainer username
- Portainer password
**Token persists for subsequent runs.**
### Non-Interactive Mode (Automation/LLM Agents)
```bash
export PORTAINER_USERNAME="admin"
export PORTAINER_PASSWORD="your-secure-password"
./update-stack.sh open-webui.yml
```
**Use this mode for:**
- CI/CD pipelines
- LLM agent workflows
- Automated deployment scripts
- Cron jobs
### Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `PORTAINER_URL` | No | `http://localhost:8080` | Portainer instance URL |
| `PORTAINER_USERNAME` | Non-interactive only | - | Admin username |
| `PORTAINER_PASSWORD` | Non-interactive only | - | Admin password |
## Examples
### Update Single Stack
```bash
./update-stack.sh open-webui.yml
```
### Update Multiple Stacks
```bash
for stack in open-webui.yml ollama.yml core-api.yml; do
./update-stack.sh "$stack"
echo "---"
done
```
### LLM Agent Integration
```bash
# Claude Code workflow example
export PORTAINER_USERNAME="admin"
export PORTAINER_PASSWORD="${PORTAINER_ADMIN_PASSWORD}" # from secure env
# Update stack after modifying YAML
./update-stack.sh open-webui.yml
# Check result
echo $? # 0 = success, 1 = failure
```
### Remote Portainer Instance
```bash
export PORTAINER_URL="https://portainer.example.com"
./update-stack.sh my-stack.yml
```
## Security Considerations
### Token Storage
- Token stored in `.portainer-token` (gitignored)
- File permissions: `600` (owner read/write only)
- Token expires based on Portainer settings (default: 8 hours)
- Re-authentication automatic if token expires
### Credentials
**DO NOT:**
- ❌ Commit `.portainer-token` to git
- ❌ Hardcode passwords in scripts
- ❌ Share tokens between users
- ❌ Use root/admin account for automation (create dedicated API user)
**DO:**
- ✅ Use environment variables for non-interactive mode
- ✅ Store credentials in secure password manager
- ✅ Create dedicated Portainer user for automation
- ✅ Rotate passwords regularly
- ✅ Use `.gitignore` to exclude token file
### Best Practices
1. **Create Automation User:**
```
Portainer → Users → Add User
Username: portainer-automation
Role: Environment Administrator (or custom)
```
2. **Use Environment Variables:**
```bash
# In ~/.bashrc or secure environment
export PORTAINER_USERNAME="portainer-automation"
export PORTAINER_PASSWORD="$(pass show portainer/automation)" # from password manager
```
3. **Restrict Permissions:**
- Grant minimum required permissions
- Limit to specific environments/stacks if possible
## Troubleshooting
### Authentication Failed
```
[ERROR] Failed to authenticate. Check credentials and try again.
```
**Solutions:**
- Verify username/password are correct
- Check Portainer is accessible: `curl http://localhost:8080/api/status`
- Ensure user has admin/environment admin role
- Try removing `.portainer-token` and re-authenticating
### Stack Not Found
```
[ERROR] Stack 'my-stack' not found in Portainer
Available stacks:
- open-webui
- ollama
- core-api
```
**Solutions:**
- Verify stack name matches filename (without .yml)
- Check stack exists in Portainer UI
- Stack name is case-sensitive
- Create stack in Portainer first if it doesn't exist
### Connection Refused
```
[ERROR] Failed to connect to Portainer at http://localhost:8080
```
**Solutions:**
- Check Portainer is running: `docker ps | grep portainer`
- Verify port: Portainer default is 8080
- Set `PORTAINER_URL` if using different port/host
- Check firewall rules if accessing remote instance
### Token Expired
```
[ERROR] Invalid authentication token
```
**Solutions:**
- Delete token file: `rm .portainer-token`
- Re-run script to re-authenticate
- Check Portainer token expiration settings
### YAML Validation Error
```
[ERROR] Stack update failed: invalid compose file
```
**Solutions:**
- Validate YAML syntax: `yamllint open-webui.yml`
- Check Docker Compose version compatibility
- Review Portainer logs: `docker logs portainer`
- Test with `docker compose config -f open-webui.yml`
## Integration with LLM Agents
### Claude Code Workflow
This script is designed to integrate seamlessly with Claude Code workflows:
1. **Agent modifies YAML file:**
```python
# Claude uses Edit tool to update open-webui.yml
```
2. **Agent calls update script:**
```bash
cd /home/jpmschweitzer/Projects/portainer-core/stacks
./update-stack.sh open-webui.yml
```
3. **Agent verifies deployment:**
```bash
docker logs open-webui --tail 20
curl http://localhost:82 # Verify service
```
### Setting Up for Claude
Add to user profile or environment:
```bash
# In ~/.bashrc or secure location
export PORTAINER_USERNAME="admin"
export PORTAINER_PASSWORD="your-secure-password"
# Or use password manager
export PORTAINER_PASSWORD="$(pass show portainer/admin)"
```
Then Claude can directly call:
```bash
./update-stack.sh <stack-file.yml>
```
## Advanced Usage
### Custom Portainer URL
```bash
# Connect to remote Portainer
export PORTAINER_URL="https://portainer.mydomain.com"
./update-stack.sh open-webui.yml
```
### Token Management
```bash
# View current token (for debugging)
cat .portainer-token | base64 -d | jq
# Force re-authentication
rm .portainer-token
./update-stack.sh open-webui.yml
# Use specific token
echo "your-jwt-token-here" > .portainer-token
chmod 600 .portainer-token
```
### Dry Run (Check Only)
```bash
# Validate YAML before updating
docker compose -f open-webui.yml config
# Check current stack status
curl -s http://localhost:8080/api/stacks \
-H "Authorization: Bearer $(cat .portainer-token)" | jq
```
## API Reference
The script uses these Portainer API endpoints:
- `POST /api/auth` - Authenticate and get token
- `GET /api/endpoints` - List Docker endpoints
- `GET /api/stacks` - List all stacks
- `PUT /api/stacks/{id}` - Update specific stack
For full API documentation: https://docs.portainer.io/api/docs
## Maintenance
### Regular Tasks
- **Monthly:** Rotate automation user password
- **Quarterly:** Review and audit API access logs
- **After incidents:** Revoke and regenerate tokens
### Token Rotation
```bash
# Revoke old token (Portainer UI)
Portainer → Users → [user] → Access Tokens → Revoke All
# Re-authenticate
rm .portainer-token
./update-stack.sh open-webui.yml
```
## Support
For issues or questions:
1. Check Portainer logs: `docker logs portainer`
2. Review this guide's Troubleshooting section
3. Check Portainer API docs: https://docs.portainer.io/api/docs
4. Open issue in project repository
---
*Last updated: 2025-11-14*