# 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:///graphql ``` **Authentication:** Header `x-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:` 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 `` and `` 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 ` **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:` prefix, or just ``? - **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:" | 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/)