16 KiB
Feature Research: Unraid GraphQL API Migration
Domain: Unraid native container management via GraphQL API Researched: 2026-02-09 Confidence: HIGH
Context
Existing system: Bot uses Docker socket proxy → Docker REST API for all container operations (status, start, stop, restart, update, logs). Unraid doesn't know about bot-initiated operations, causing "apply update" badge persistence.
Migration target: Replace Docker socket proxy with Unraid's native GraphQL API for all operations. Unraid 7.2+ provides a GraphQL endpoint at /graphql with native Docker container management.
Key question: Which existing features are drop-in replacements (same capability, different API) vs. which gain new capabilities vs. which need workarounds?
Feature Landscape
Direct Replacements (Same Behavior, Different API)
Features that work identically via Unraid API — no user-visible changes.
| Feature | Current Implementation | Unraid API Equivalent | Complexity | Notes |
|---|---|---|---|---|
| Container status display | GET /containers/json → parse JSON → display |
query { docker { containers { id names state } } } |
LOW | GraphQL returns structured data, cleaner parsing. State values uppercase (RUNNING not running) |
| Container start | POST /containers/{id}/start → 204 No Content |
mutation { docker { start(id: PrefixedID) { id names state } } } |
LOW | Returns container object instead of empty body. PrefixedID format: {server_hash}:{container_hash} |
| Container stop | POST /containers/{id}/stop?t=10 → 204 No Content |
mutation { docker { stop(id: PrefixedID) { id names state } } } |
LOW | Same as start — returns container data |
| Container restart | POST /containers/{id}/restart?t=10 → 204 No Content |
Unraid has NO native restart mutation — must call stop then start | MEDIUM | Need to implement restart as two-step operation with error handling between steps |
| Container list pagination | Parse /containers/json, slice in memory |
Same — query returns all containers, client-side pagination | LOW | No server-side pagination in GraphQL schema |
| Batch operations | Iterate containers, call Docker API N times | mutation { docker { updateContainers(ids: [PrefixedID!]!) } } for updates, iterate for start/stop |
MEDIUM | Batch update is native, batch start/stop still requires iteration |
Enhanced Features (Gain New Capabilities)
Features that work better with Unraid API.
| Feature | New Capability | Value | Complexity | Notes |
|---|---|---|---|---|
| Container update | Automatic update status sync — Unraid knows bot updated container, no "apply update" badge | Solves core v1.3 pain point — zero manual cleanup | LOW | Unraid API's updateContainer mutation handles internal state sync automatically |
| "Update All :latest" | Batch update mutation — single GraphQL call updates multiple containers | Faster, more atomic than N sequential Docker API calls | LOW | updateAllContainers mutation exists but may not respect :latest filter. May need updateContainers(ids: [...]) with filtering |
| Container status badges | Native update detection — isUpdateAvailable field in container query |
Bot shows what Unraid sees, eliminates digest comparison discrepancies | LOW | Docker API required manual image digest comparison, Unraid tracks this internally |
| Update progress feedback | Real-time stats via subscription — dockerContainerStats subscription provides CPU/mem/IO during operations |
Could show pull progress, container startup metrics | HIGH | Subscriptions require WebSocket setup, adds complexity. DEFER to future phase |
Features Requiring Workarounds
Features where Unraid API is less capable than Docker API.
| Feature | Docker API Approach | Unraid API Limitation | Workaround | Complexity | Impact |
|---|---|---|---|---|---|
| Container logs | GET /containers/{id}/logs?stdout=1&stderr=1&tail=N×tamps=1 |
query { docker { logs(id: PrefixedID, tail: Int, since: DateTime) { ... } } } |
Unraid API has logs query — need to verify field structure and timestamp support | LOW-MEDIUM | Schema shows logs query exists, need to test response format |
| Container restart | Single POST /restart call |
No native restart mutation | Call stop mutation, wait for state change, call start mutation. Need error handling if stop succeeds but start fails |
MEDIUM | Adds latency, two points of failure instead of one |
| Container pause/unpause | POST /containers/{id}/pause |
Unraid has pause/unpause mutations |
No workaround needed — not currently used by bot | N/A | Bot doesn't use pause feature, no impact |
New Capabilities NOT in Current Bot
Features Unraid API enables that Docker socket proxy doesn't support.
| Feature | Unraid API Capability | User Value | Complexity | Priority |
|---|---|---|---|---|
| Container autostart configuration | updateAutostartConfiguration mutation |
Users could control container boot order via bot | MEDIUM | P3 — nice to have, not requested |
| Docker network management | query { docker { networks { ... } } } |
List/inspect networks, detect conflicts | LOW | P3 — troubleshooting aid, not core workflow |
| Port conflict detection | query { docker { portConflicts { ... } } } |
Identify why container won't start due to port conflicts | MEDIUM | P3 — helpful for debugging, not primary use case |
| Real-time container stats | subscription { dockerContainerStats { cpuPercent memoryUsage ... } } |
Live resource monitoring during updates | HIGH | P3 — requires WebSocket infrastructure |
Feature Dependencies
Container Operations (start/stop/update)
└──requires──> PrefixedID format mapping
└──requires──> Container ID resolution (existing matching logic)
Batch Update
└──requires──> Container selection UI (existing)
└──enhances──> "Update All :latest" (atomic operation)
Update Status Sync
└──automatically provided by──> Unraid API mutations (no explicit action needed)
└──eliminates need for──> File writes to /var/lib/docker/unraid-update-status.json
Container Restart
└──requires──> Stop mutation
└──requires──> Start mutation
└──requires──> State polling between operations
Container Logs
└──requires──> GraphQL logs query testing
└──may require──> Response format adaptation (if different from Docker API)
Dependency Notes
- PrefixedID format is critical: Unraid uses
{server_hash}:{container_hash}(128-char total) instead of Docker's short container ID. Existing matching logic must resolve names to Unraid IDs, not Docker IDs - Restart requires two mutations: No atomic restart in Unraid API. Must implement stop → verify → start pattern
- Update status sync is automatic: Biggest win — no manual file manipulation needed, Unraid knows about updates immediately
- Logs query needs verification: Schema shows
logsexists but field structure unknown until tested
Migration Complexity Assessment
Drop-in Replacements (LOW complexity)
Change API endpoint and request format, behavior unchanged.
- Container list/status display
- Container start
- Container stop
- Batch container selection UI (no API changes)
- Confirmation dialogs (no API changes)
Effort: 1-2 nodes per operation. Replace HTTP Request URL and body, adapt response parsing. Error handling pattern stays same.
Adapted Replacements (MEDIUM complexity)
Requires implementation changes but same user experience.
- Container restart — Implement as stop + start sequence with state verification
- Container logs — Adapt to GraphQL logs query response format
- Batch update — Use
updateContainers(ids: [...])mutation instead of N individual calls - Container ID resolution — Map container names to PrefixedID format
Effort: 3-5 nodes per operation. Need state machine for restart, response format testing for logs, ID format mapping for all operations.
Enhanced Features (LOW-MEDIUM complexity)
Gain new capabilities with minimal work.
- Update status sync — Automatic via Unraid API, remove Phase 14 manual sync
- Update detection — Use
isUpdateAvailablefield instead of Docker digest comparison - Batch mutations — Native support for multi-container updates
Effort: Remove old workarounds, use new API fields. Net simplification.
Migration Phases
Phase 1: Infrastructure (Phase 14 — COMPLETE)
- Unraid GraphQL API connectivity
- Authentication setup (API key, Header Auth credential)
- Test query validation
- Container ID format documentation
Status: Complete per Phase 14 verification. Ready for mutation implementation.
Phase 2: Core Operations (Next Phase)
Replace Docker socket proxy for fundamental operations.
- Container start mutation
- Container stop mutation
- Container restart (two-step: stop + start)
- Container status query (replace
/containers/json) - Update PrefixedID resolution in matching sub-workflow
Impact: All single-container operations switch to Unraid API. Docker socket proxy only used for updates and logs temporarily.
Phase 3: Update Operations
Replace update workflow with Unraid API.
- Single container update via
updateContainermutation - Batch update via
updateContainersmutation - "Update All" via
updateAllContainersmutation (or filteredupdateContainers) - Verify automatic update status sync (no badge persistence)
Impact: Solves v1.3 milestone pain point. Unraid UI reflects bot updates immediately.
Phase 4: Logs and Polish
Replace remaining Docker API calls.
- Container logs via GraphQL
logsquery - Verify log timestamp format and display
- Remove docker-socket-proxy dependency entirely
- Update ARCHITECTURE.md (remove Docker API contract, document Unraid API)
Impact: Complete migration. Docker socket proxy container can be removed.
Complexity Matrix
| Operation | Docker API | Unraid API | Complexity | Blocker |
|---|---|---|---|---|
| Start | POST /start | mutation start(id) | LOW | None |
| Stop | POST /stop | mutation stop(id) | LOW | None |
| Restart | POST /restart | stop + start (2 calls) | MEDIUM | State verification between mutations |
| Status | GET /json | query containers | LOW | PrefixedID format mapping |
| Update | POST /images/create + stop + rename + start | mutation updateContainer(id) | LOW | None — simpler than Docker API |
| Batch Update | N × update | mutation updateContainers(ids) | LOW | None — native support |
| Logs | GET /logs | query logs(id, tail, since) | MEDIUM | Response format unknown |
Key insight: Most operations are simpler with Unraid API. Only restart and logs require adaptation work.
Anti-Features
Features that seem useful but complicate migration without user value.
| Feature | Why Tempting | Why Problematic | Alternative |
|---|---|---|---|
| Parallel use of Docker API + Unraid API | "Keep both during migration" | Two sources of truth, complex ID mapping, defeats purpose of migration | Full cutover per operation — start/stop on Unraid API, then update, then logs |
| GraphQL subscriptions for real-time stats | "Monitor container resource usage live" | Requires WebSocket setup, n8n HTTP Request node doesn't support subscriptions, adds infrastructure complexity | Poll if needed, defer to future phase with dedicated subscription node |
| Expose full GraphQL schema to user | "Let users run arbitrary queries via bot" | Security risk (unrestricted API access), complex query parsing, unclear user benefit | Expose only operations via commands (start, update, logs), not raw GraphQL |
| Port conflict detection on every status check | "Proactively warn about port conflicts" | Performance impact (extra query), rare occurrence, clutters UI | Only query port conflicts when start/restart fails with port binding error |
Success Criteria
Migration is successful when:
- Zero Docker socket proxy calls — All operations use Unraid GraphQL API
- Update badge sync works — Unraid UI shows correct status after bot updates
- Restart works reliably — Two-step restart handles edge cases (stop succeeds, start fails)
- Logs display correctly — GraphQL logs query returns usable data for Telegram display
- No performance regression — Operations complete in same or better time than Docker API
- Error messages stay clear — GraphQL errors map to actionable user feedback
Sources
Primary (HIGH confidence)
- Unraid GraphQL Schema — Docker mutations (start, stop, pause, unpause, updateContainer, updateContainers, updateAllContainers), queries (containers, logs, portConflicts), subscriptions (dockerContainerStats)
- Using the Unraid API — Endpoint URL, authentication, rate limiting
- Docker and VM Integration | Unraid API — DockerService architecture, retry logic, timeout handling
- Phase 14 Research (
14-RESEARCH.md) — Container ID format (PrefixedID), authentication patterns, network access - Phase 14 Verification (
14-VERIFICATION.md) — Confirmed working query, credential setup, myunraid.net URL requirement
Secondary (MEDIUM confidence)
- Core Services | Unraid API — DockerService mutation implementation details
- Existing bot architecture (
ARCHITECTURE.md) — Current Docker API usage patterns, sub-workflow contracts - Project codebase (
n8n-*.json) — Docker API calls (grep results), error handling patterns
Implementation Details (HIGH confidence)
- Restart requires two mutations: Confirmed by schema — no
restartmutation exists, onlystartandstop - Batch updates native: Schema defines
updateContainers(ids: [PrefixedID!]!)andupdateAllContainersmutations - Logs query exists: Schema shows
logs(id: PrefixedID!, since: DateTime, tail: Int)→DockerContainerLogs!type - Real-time stats via subscription:
dockerContainerStatssubscription exists but requires WebSocket transport
Open Questions
-
DockerContainerLogs response structure
- What we know: Schema defines type, accepts
sinceandtailparams - What's unclear: Field names, timestamp format, stdout/stderr separation
- Resolution: Test logs query in Phase 2/3, adapt parsing logic as needed
- What we know: Schema defines type, accepts
-
updateAllContainers behavior
- What we know: Mutation exists, returns
[DockerContainer!]! - What's unclear: Does it filter by
:latesttag, or update everything with available updates? - Resolution: Test mutation or use
updateContainers(ids)with manual filtering
- What we know: Mutation exists, returns
-
Restart failure scenarios
- What we know: Must implement as stop + start
- What's unclear: Best retry/backoff pattern if start fails after stop succeeds
- Resolution: Design state machine with error recovery (Phase 2 planning)
-
Rate limiting for batch operations
- What we know: Unraid API has rate limiting (docs confirm)
- What's unclear: Does
updateContainerscount as 1 request or N requests? - Resolution: Test batch update with 20+ containers, monitor for 429 errors
Feature research for: Unraid GraphQL API migration Researched: 2026-02-09 Milestone: Replace Docker socket proxy with Unraid native API