Files
unraid-docker-manager/.planning/research/STACK.md
T

396 lines
15 KiB
Markdown

# Technology Stack — Unraid Update Status Sync
**Project:** Unraid Docker Manager
**Milestone:** Update Status Sync (v1.3)
**Researched:** 2026-02-08
## Executive Summary
**Recommendation:** Use Unraid's native GraphQL API with `updateContainer` mutation to sync status after bot-initiated container updates.
This approach leverages Unraid's official API (available since 7.2, via Connect plugin for <7.2) to call the same update mechanism the WebGUI uses. The GraphQL `updateContainer` mutation triggers Dynamix Docker Manager's update workflow, which automatically handles image digest comparison and updates `/var/lib/docker/unraid-update-status.json`.
**Confidence:** HIGH — Based on official Unraid API documentation, source code analysis, and GraphQL schema.
---
## Recommended Stack Additions
### 1. Unraid GraphQL API Client
| Component | Version | Purpose | Why |
|-----------|---------|---------|-----|
| HTTP Request (n8n built-in) | n8n 1.x | GraphQL API calls | Already available, no new dependencies |
| Unraid API | 7.2+ or Connect plugin | Container update status sync | Official Unraid API, supported mechanism |
**Installation:** None required in n8n container. Requires Unraid API key creation on host.
**Network Access:**
- From n8n container → Unraid host: `http://host.docker.internal/graphql` (requires `--add-host=host.docker.internal:host-gateway`)
- Or use Unraid server IP: `http://192.168.x.x/graphql` or `http://tower.local/graphql`
### 2. Authentication
| Component | Purpose | Storage | Why |
|-----------|---------|---------|-----|
| Unraid API Key | GraphQL authentication | `.env.unraid-api` (gitignored) | Same pattern as n8n API credentials |
**Permissions Required:**
```bash
unraid-api apikey --create \
--name "Docker Manager Bot" \
--permissions "DOCKER:UPDATE_ANY" \
--description "Telegram bot container updates" \
--json
```
Minimum permission: `DOCKER:UPDATE_ANY` (allows calling `updateContainer` mutation)
---
## Integration Architecture
### How Unraid Tracks Update Status
**Three-layer system:**
1. **Image Digest Comparison** — Unraid's `DockerManifestService` compares local image digest with registry manifest
2. **Update Status File**`/var/lib/docker/unraid-update-status.json` stores per-container status (`true` = up-to-date, `false` = update available, `undef` = unknown)
3. **Template Metadata** — XML templates in `/boot/config/plugins/dockerMan/templates-user/` store container configuration and version info
**Critical Finding:** Unraid does NOT auto-detect external updates (e.g., via Watchtower or direct Docker API). The `updateContainer` mutation must be called to sync status.
**Source:** [limetech/dynamix DockerClient.php](https://github.com/limetech/dynamix/blob/master/plugins/dynamix.docker.manager/include/DockerClient.php) — `syncVersions()` function updates status file after successful update.
### Update Sync Workflow
```
Bot updates container (current approach)
↓ docker pull + recreate via Docker API
↓ Container running new image
↓ Unraid still shows "update available" ← PROBLEM
Call Unraid GraphQL API (new addition)
↓ mutation { docker { updateContainer(id: "...") { ... } } }
↓ Unraid's DockerService → Dynamix scripts
↓ syncVersions() writes to unraid-update-status.json
↓ Unraid UI shows "up-to-date" ← SOLVED
```
**Idempotency:** Safe to call `updateContainer` even if already updated — Unraid checks current vs. remote digest and no-ops if already synced.
---
## GraphQL API Details
### Endpoint
```
http://<unraid-host>/graphql
```
**Authentication:** Header `x-api-key: <your-api-key>`
### Key Mutations
```graphql
mutation UpdateSingleContainer {
docker {
updateContainer(id: "docker:HmiXBlJefBRPMS0m4iNYc") {
id
name
state
}
}
}
mutation UpdateMultipleContainers {
docker {
updateContainers(ids: ["docker:abc123", "docker:def456"]) {
id
name
state
}
}
}
```
**Container ID Format:** `docker:<container_name>` or retrieve via query first.
**Source:** [Unraid API generated-schema.graphql](https://raw.githubusercontent.com/unraid/api/main/api/generated-schema.graphql)
### n8n Integration Pattern
**Node:** HTTP Request (built-in)
**Configuration:**
- Method: `POST`
- URL: `http://host.docker.internal/graphql` (or Unraid IP)
- Authentication: Header Auth
- Name: `x-api-key`
- Value: `{{$env.UNRAID_API_KEY}}` (from `.env.unraid-api`)
- Body (JSON):
```json
{
"query": "mutation { docker { updateContainer(id: \"docker:{{$json.containerName}}\") { id name state } } }"
}
```
**Data Flow:**
```
Telegram callback (container updated)
→ Extract container name
→ HTTP Request to Unraid GraphQL
→ Parse response
→ Telegram confirmation message
```
---
## Alternatives Considered
| Approach | Pros | Cons | Verdict |
|----------|------|------|---------|
| **GraphQL API `updateContainer`** | Official API, handles all sync logic, idempotent, supported | Requires API key setup | **RECOMMENDED** |
| **Direct file write to `unraid-update-status.json`** | Simple, no auth needed | Brittle (file format undocumented), doesn't trigger UI refresh, race conditions with Unraid's update checker | **DO NOT USE** |
| **Shell exec via Docker socket** | Could call Dynamix scripts directly | Extremely fragile, requires PHP runtime, breaks on Unraid updates | **DO NOT USE** |
| **Watchtower pattern (do nothing)** | No code changes | Leaves stale "update available" badges, false-positive notifications | **Current problem** |
| **GraphQL `updateAllContainers`** | Syncs everything | Overkill (re-checks all containers), slower | **Use for batch sync only** |
---
## File System Access (NOT Recommended)
**Investigated but rejected:**
### `/var/lib/docker/unraid-update-status.json`
**Structure (observed):**
```json
{
"container1": true,
"container2": false,
"container3": "undef"
}
```
**Why not write directly:**
1. **Format undocumented** — File structure found via forum posts, not official docs
2. **No UI refresh trigger** — Writing file doesn't notify WebGUI to refresh
3. **Race conditions** — Unraid's update checker runs periodically, may overwrite changes
4. **Brittle** — File location/format could change without notice
**Confidence:** MEDIUM (structure inferred from forum posts, confirmed in source code)
**Source:** [Unraid Forums — Docker Update Status](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/page/9/)
### `/boot/config/plugins/dockerMan/templates-user/`
**Contains:** XML templates with container configuration, including `<Date>` and `<DateInstalled>` fields (internal, auto-managed by Unraid).
**Why not modify:** Templates are for configuration, not runtime status. Unraid ignores template dates for update checking (uses image digests instead).
---
## Network Configuration
### Docker Network Modes in Unraid
| Mode | Access Unraid Host | Use Case |
|------|-------------------|----------|
| **Bridge** | Via `172.17.0.1` (gateway) or `host.docker.internal` | Default, port mapping |
| **Host** | Via `localhost` | Direct host network access |
| **Custom:br0** | Via Unraid IP (macvlan, separate network) | Own IP on LAN |
**n8n container likely uses:** Bridge mode (most common for Unraid containers)
### Accessing Unraid API from n8n Container
**Option 1: host.docker.internal (recommended)**
```bash
# Add to n8n container config (via Unraid Docker template):
--add-host=host.docker.internal:host-gateway
# Then in n8n workflow:
URL: http://host.docker.internal/graphql
```
**Option 2: Unraid hostname/IP**
```
URL: http://tower.local/graphql
# or
URL: http://192.168.1.100/graphql # Replace with actual Unraid IP
```
**Verification:**
```bash
# From inside n8n container:
docker exec -it n8n curl -I http://host.docker.internal/graphql
# Should return HTTP 400 (GraphQL requires POST) or 200
```
**Source:** [Docker host.docker.internal guide](https://eastondev.com/blog/en/posts/dev/20251217-docker-host-access/)
---
## Security Considerations
### API Key Management
**Storage:**
- Add to `.env.unraid-api` (already gitignored)
- Load in n8n workflow via environment variables
**Permissions:**
- Use **least privilege**: `DOCKER:UPDATE_ANY` only
- Avoid `--roles ADMIN` (grants full access)
**Rotation:**
- API keys don't expire, but should be rotated periodically
- Delete via CLI: `unraid-api apikey --delete <key-id>`
**Source:** [Unraid API Key Management](https://docs.unraid.net/API/programmatic-api-key-management/)
### Network Exposure
**Unraid API runs on:**
- HTTP (port 80) or HTTPS (port 443) on Unraid host
- Same interface as WebGUI
**Risk:** n8n container can access full GraphQL API (not just Docker mutations)
**Mitigation:** Use scoped API key (limits permissions to Docker operations only)
---
## Implementation Checklist
- [ ] Create Unraid API key with `DOCKER:UPDATE_ANY` permission
- [ ] Add API key to `.env.unraid-api` file
- [ ] Verify n8n container can reach `http://host.docker.internal/graphql` (add `--add-host` if needed)
- [ ] Create HTTP Request credential in n8n (Header Auth with `x-api-key`)
- [ ] Add GraphQL mutation call to Update sub-workflow after successful Docker API update
- [ ] Test: Update container via bot → verify Unraid UI shows "up-to-date"
- [ ] Handle errors: GraphQL response parsing, network failures, auth errors
---
## What NOT to Add
**Do NOT add:**
- ❌ File system watchers for `/var/lib/docker/unraid-update-status.json`
- ❌ Custom PHP scripts to call Dynamix functions
- ❌ Cron jobs to periodically sync status
- ❌ Docker socket mounting to execute Unraid shell commands
- ❌ Watchtower or similar auto-update tools (conflicts with bot's update control)
**Why:** These approaches are brittle, unsupported, or conflict with the bot's explicit update model.
---
## Version Compatibility
| Unraid Version | API Availability | Notes |
|----------------|-----------------|-------|
| 7.2+ | Built-in | Native GraphQL API |
| 6.9 - 7.1 | Via Connect plugin | Install from Community Applications |
| <6.9 | Not available | Upgrade Unraid required |
**Check version:**
```bash
# SSH to Unraid:
cat /etc/unraid-version
```
**Install Connect plugin (pre-7.2):**
1. Apps → Search "Unraid Connect"
2. Install plugin
3. Settings → Management Access → API → Enable
**Source:** [Unraid API Documentation](https://docs.unraid.net/API/)
---
## Open Questions & Validation Needed
**Low-priority research flags:**
1. **Container ID resolution** — Does GraphQL require `docker:<name>` prefix, or just `<name>`?
- **Resolution:** Query containers first to get exact ID format
- **Impact:** Low (easily testable during implementation)
2. **Rate limiting** — Does Unraid API have rate limits for mutations?
- **Confidence:** LOW (not documented)
- **Impact:** Low (bot updates are infrequent, <10/min even in batch mode)
3. **GraphQL subscription for status changes** — Could bot subscribe to Docker events?
- **Finding:** Schema includes subscriptions, but not needed for status sync
- **Verdict:** Defer to future enhancement (real-time status monitoring)
---
## Sources & Confidence Assessment
| Finding | Confidence | Primary Source |
|---------|-----------|----------------|
| GraphQL API exists with updateContainer mutation | HIGH | [Unraid API Schema](https://raw.githubusercontent.com/unraid/api/main/api/generated-schema.graphql) |
| updateContainer triggers Dynamix sync workflow | HIGH | [DeepWiki — Docker Integration](https://deepwiki.com/unraid/api/2.4-docker-integration) |
| syncVersions() updates status JSON file | HIGH | [limetech/dynamix source](https://github.com/limetech/dynamix/blob/master/plugins/dynamix.docker.manager/include/DockerClient.php) |
| API key requires DOCKER:UPDATE_ANY permission | MEDIUM | [API Key Management](https://docs.unraid.net/API/programmatic-api-key-management/) (exact permission not explicitly stated) |
| Container ID format is "docker:<name>" | MEDIUM | [Unraid API examples](https://github.com/domalab/unraid-api-client/blob/main/UNRAIDAPI.md) (needs verification) |
| host.docker.internal works in Unraid Docker | MEDIUM | [Forum discussions](https://forums.unraid.net/topic/95050-how-to-use-the-hosts-ip-address-inside-a-docker-container/) (standard Docker feature, but Unraid-specific confirmation limited) |
| /var/lib/docker/unraid-update-status.json structure | MEDIUM | [Forum posts](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/page/9/) (observed, not officially documented) |
| No rate limiting on GraphQL API | LOW | Assumption (not documented either way) |
**Overall Confidence: HIGH** — Core approach (GraphQL API) is well-documented and official. Implementation details need testing but low-risk.
---
## Next Steps for Roadmap
**Phase structure recommendations:**
1. **Phase 1: API Setup & Testing**
- Create API key
- Test GraphQL query/mutation from command line
- Verify network access from n8n container
- **Why first:** Validates approach before n8n integration
2. **Phase 2: n8n Integration**
- Add HTTP Request node to Update sub-workflow
- GraphQL mutation call after Docker update
- Error handling for API failures
- **Why second:** Builds on validated API access
3. **Phase 3: Batch Sync Support**
- Optional: Call `updateContainers` (plural) for batch updates
- Handle partial failures (some containers sync, others fail)
- **Why third:** Enhancement after core functionality works
**Research flags:**
- Phase 1: Likely needs deeper research (network access testing, container ID format verification)
- Phase 2: Standard patterns, unlikely to need additional research
- Phase 3: Defer until Phase 2 validates approach
---
## Additional Resources
**Official Documentation:**
- [Unraid API Overview](https://docs.unraid.net/API/)
- [Using the Unraid API](https://docs.unraid.net/API/how-to-use-the-api/)
- [GraphQL Schema (raw)](https://raw.githubusercontent.com/unraid/api/main/api/generated-schema.graphql)
- [API Key Management](https://docs.unraid.net/API/programmatic-api-key-management/)
**Community Resources:**
- [Unraid MCP Server](https://github.com/jmagar/unraid-mcp) — Reference implementation of GraphQL client
- [Home Assistant Unraid Integration](https://github.com/domalab/unraid-api-client) — Another GraphQL client example
**Source Code:**
- [limetech/dynamix](https://github.com/limetech/dynamix) — Unraid's Docker Manager plugin
- [unraid/api](https://github.com/unraid/api) — Official API monorepo
**Forum Discussions:**
- [Watchtower not syncing status](https://github.com/containrrr/watchtower/discussions/1389)
- [Docker update status not reflected](https://forums.unraid.net/topic/149953-docker-update-via-watchtower-status-not-reflected-in-unraid/)