docs: complete v1.3 project research (STACK, FEATURES, ARCHITECTURE, PITFALLS, SUMMARY)
This commit is contained in:
+314
-304
@@ -1,385 +1,395 @@
|
||||
# Stack Research: v1.1 Features
|
||||
# Technology Stack — Unraid Update Status Sync
|
||||
|
||||
**Project:** Unraid Docker Manager
|
||||
**Researched:** 2026-02-02
|
||||
**Focus:** Stack additions for n8n API, Docker socket security, Telegram keyboards, Unraid integration
|
||||
**Milestone:** Update Status Sync (v1.3)
|
||||
**Researched:** 2026-02-08
|
||||
|
||||
## n8n API Access
|
||||
## Executive Summary
|
||||
|
||||
### Overview
|
||||
**Recommendation:** Use Unraid's native GraphQL API with `updateContainer` mutation to sync status after bot-initiated container updates.
|
||||
|
||||
n8n provides a public REST API for programmatic workflow management. This enables Claude Code to read, update, and test workflows without manual UI interaction.
|
||||
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`.
|
||||
|
||||
### Authentication
|
||||
|
||||
**Method:** API Key via HTTP header
|
||||
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| Header name | `X-N8N-API-KEY` |
|
||||
| Key location | n8n UI: Settings > n8n API > Create an API key |
|
||||
| Base path | `/api/v1` |
|
||||
|
||||
**Environment Variables:**
|
||||
- `N8N_PUBLIC_API_DISABLED=false` (default) - API enabled
|
||||
- `N8N_PUBLIC_API_SWAGGERUI_DISABLED=false` (default) - Swagger UI enabled
|
||||
|
||||
The API is **enabled by default** on self-hosted n8n. No additional configuration needed unless it was explicitly disabled.
|
||||
|
||||
### Key Endpoints
|
||||
|
||||
| Endpoint | Method | Purpose |
|
||||
|----------|--------|---------|
|
||||
| `/api/v1/workflows` | GET | List all workflows |
|
||||
| `/api/v1/workflows/{id}` | GET | Get workflow JSON |
|
||||
| `/api/v1/workflows/{id}` | PUT | Update workflow |
|
||||
| `/api/v1/workflows/{id}/activate` | POST | Activate workflow |
|
||||
| `/api/v1/workflows/{id}/deactivate` | POST | Deactivate workflow |
|
||||
| `/api/v1/executions` | GET | List executions (with logs) |
|
||||
| `/api/v1/executions/{id}` | GET | Get execution details |
|
||||
|
||||
### API Playground
|
||||
|
||||
Self-hosted n8n includes a built-in Swagger UI at `/api/v1/docs` (or similar path based on configuration). This provides interactive documentation for testing API calls.
|
||||
|
||||
### Integration with Claude Code
|
||||
|
||||
To enable Claude Code workflow management:
|
||||
|
||||
1. Create API key in n8n Settings > n8n API
|
||||
2. Store key securely (not in repository)
|
||||
3. Use HTTP requests to n8n API endpoints
|
||||
4. Example: `curl -H "X-N8N-API-KEY: <key>" http://localhost:5678/api/v1/workflows`
|
||||
|
||||
**Confidence:** MEDIUM - Official docs confirm API exists and authentication method. Specific endpoint paths verified through multiple sources but not directly tested.
|
||||
|
||||
**Sources:**
|
||||
- [n8n API Authentication](https://docs.n8n.io/api/authentication/)
|
||||
- [n8n API Reference](https://docs.n8n.io/api/api-reference/)
|
||||
- [Disable Public API](https://docs.n8n.io/hosting/securing/disable-public-api/)
|
||||
**Confidence:** HIGH — Based on official Unraid API documentation, source code analysis, and GraphQL schema.
|
||||
|
||||
---
|
||||
|
||||
## Docker Socket Security
|
||||
## Recommended Stack Additions
|
||||
|
||||
### The Problem
|
||||
### 1. Unraid GraphQL API Client
|
||||
|
||||
Current setup mounts Docker socket directly into internet-exposed n8n container:
|
||||
```
|
||||
-v /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
| 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 |
|
||||
|
||||
This is a security risk: if n8n is compromised, attacker has full Docker (root) access to the host.
|
||||
**Installation:** None required in n8n container. Requires Unraid API key creation on host.
|
||||
|
||||
### Solution: Docker Socket Proxy
|
||||
**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`
|
||||
|
||||
A socket proxy sits between containers and the Docker socket, filtering API requests to only allow specific operations.
|
||||
### 2. Authentication
|
||||
|
||||
### Option Comparison
|
||||
|
||||
| Feature | Tecnativa | LinuxServer | Wollomatic |
|
||||
|---------|-----------|-------------|------------|
|
||||
| Base | HAProxy (Alpine) | HAProxy (Alpine) | Go (scratch) |
|
||||
| Image size | ~10MB | ~10MB | ~3MB |
|
||||
| Config method | Environment variables | Environment variables | Regex allowlists |
|
||||
| Granularity | Per-API section | Per-API section | Per-endpoint regex |
|
||||
| Active maintenance | Yes | Yes | Yes |
|
||||
| Unraid compatibility | Yes | Yes | Yes |
|
||||
|
||||
### Recommendation: LinuxServer/socket-proxy
|
||||
|
||||
**Why:** Drop-in replacement for Tecnativa with better documentation and active LinuxServer community support. Familiar to Unraid users.
|
||||
|
||||
### Configuration for This Project
|
||||
|
||||
The bot needs these Docker API operations:
|
||||
- List containers (`/containers/json`)
|
||||
- Inspect container (`/containers/{id}/json`)
|
||||
- Start container (`/containers/{id}/start`)
|
||||
- Stop container (`/containers/{id}/stop`)
|
||||
- Restart container (`/containers/{id}/restart`)
|
||||
- Pull image (`/images/create`)
|
||||
- Create container (`/containers/create`)
|
||||
- Remove container (`/containers/{id}`)
|
||||
- Container logs (`/containers/{id}/logs`)
|
||||
|
||||
**Required Environment Variables:**
|
||||
| Component | Purpose | Storage | Why |
|
||||
|-----------|---------|---------|-----|
|
||||
| Unraid API Key | GraphQL authentication | `.env.unraid-api` (gitignored) | Same pattern as n8n API credentials |
|
||||
|
||||
**Permissions Required:**
|
||||
```bash
|
||||
# LinuxServer socket-proxy configuration
|
||||
CONTAINERS=1 # Container list/inspect
|
||||
IMAGES=1 # Image pull
|
||||
POST=1 # Enable POST requests (needed for start/stop/restart/create)
|
||||
ALLOW_START=1 # /containers/{id}/start
|
||||
ALLOW_STOP=1 # /containers/{id}/stop
|
||||
ALLOW_RESTARTS=1 # /containers/{id}/restart (also enables /kill)
|
||||
|
||||
# Keep defaults (already enabled)
|
||||
EVENTS=1 # Default
|
||||
PING=1 # Default
|
||||
VERSION=1 # Default
|
||||
unraid-api apikey --create \
|
||||
--name "Docker Manager Bot" \
|
||||
--permissions "DOCKER:UPDATE_ANY" \
|
||||
--description "Telegram bot container updates" \
|
||||
--json
|
||||
```
|
||||
|
||||
### Deployment Architecture
|
||||
|
||||
```
|
||||
[Internet]
|
||||
|
|
||||
[n8n container]
|
||||
|
|
||||
[socket-proxy] <-- internal network only
|
||||
|
|
||||
[Docker socket]
|
||||
```
|
||||
|
||||
**n8n container changes:**
|
||||
- Remove: `-v /var/run/docker.sock:/var/run/docker.sock`
|
||||
- Add: `DOCKER_HOST=tcp://socket-proxy:2375`
|
||||
- Add: Connect to same Docker network as socket-proxy
|
||||
|
||||
**Socket-proxy container:**
|
||||
```bash
|
||||
docker run -d \
|
||||
--name socket-proxy \
|
||||
--restart=unless-stopped \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
--read-only \
|
||||
--tmpfs /run \
|
||||
-e CONTAINERS=1 \
|
||||
-e IMAGES=1 \
|
||||
-e POST=1 \
|
||||
-e ALLOW_START=1 \
|
||||
-e ALLOW_STOP=1 \
|
||||
-e ALLOW_RESTARTS=1 \
|
||||
--network internal \
|
||||
lscr.io/linuxserver/socket-proxy:latest
|
||||
```
|
||||
|
||||
**CRITICAL:** Never expose socket-proxy port (2375) to external networks. Use internal Docker network only.
|
||||
|
||||
**Confidence:** HIGH - Official documentation from both Tecnativa and LinuxServer confirms configuration options and security model.
|
||||
|
||||
**Sources:**
|
||||
- [Tecnativa docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
- [LinuxServer socket-proxy](https://docs.linuxserver.io/images/docker-socket-proxy/)
|
||||
- [Wollomatic socket-proxy](https://github.com/wollomatic/socket-proxy)
|
||||
Minimum permission: `DOCKER:UPDATE_ANY` (allows calling `updateContainer` mutation)
|
||||
|
||||
---
|
||||
|
||||
## Telegram Inline Keyboards
|
||||
## Integration Architecture
|
||||
|
||||
### The Problem
|
||||
### How Unraid Tracks Update Status
|
||||
|
||||
n8n's native Telegram node has limitations with inline keyboards:
|
||||
1. Cannot pass dynamic JSON for `reply_markup`
|
||||
2. Expressions in keyboard fields cause "The value is not supported!" errors
|
||||
3. PR #17258 adding JSON keyboard support has been pending since July 2025
|
||||
**Three-layer system:**
|
||||
|
||||
### Solution Options
|
||||
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
|
||||
|
||||
| Option | Approach | Pros | Cons |
|
||||
|--------|----------|------|------|
|
||||
| HTTP Request node | Direct Telegram API calls | Full control, no dependencies | Token in URL, more setup |
|
||||
| Custom community node | @topvisor/n8n-nodes-telegram-send-message-custom | Easy JSON support | External dependency |
|
||||
| Wait for PR #17258 | Native n8n support | No workarounds needed | Indefinite timeline |
|
||||
**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.
|
||||
|
||||
### Recommendation: HTTP Request Node
|
||||
**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.
|
||||
|
||||
**Why:** No external dependencies, full Telegram API access, already proven pattern in similar projects.
|
||||
### Update Sync Workflow
|
||||
|
||||
### Implementation
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
**Send Message with Inline Keyboard:**
|
||||
**Idempotency:** Safe to call `updateContainer` even if already updated — Unraid checks current vs. remote digest and no-ops if already synced.
|
||||
|
||||
```json
|
||||
// HTTP Request node
|
||||
// URL: https://api.telegram.org/bot{{ $credentials.telegramApi.token }}/sendMessage
|
||||
// Method: POST
|
||||
// Body Type: JSON
|
||||
{
|
||||
"chat_id": "={{ $json.message.chat.id }}",
|
||||
"text": "Select a container:",
|
||||
"parse_mode": "HTML",
|
||||
"reply_markup": {
|
||||
"inline_keyboard": [
|
||||
[
|
||||
{"text": "plex", "callback_data": "start:plex"},
|
||||
{"text": "sonarr", "callback_data": "start:sonarr"}
|
||||
],
|
||||
[
|
||||
{"text": "radarr", "callback_data": "start:radarr"},
|
||||
{"text": "Cancel", "callback_data": "cancel"}
|
||||
]
|
||||
]
|
||||
---
|
||||
|
||||
## 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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Handle Callback Query:**
|
||||
**Container ID Format:** `docker:<container_name>` or retrieve via query first.
|
||||
|
||||
The workflow already handles `callback_query` via the Telegram Trigger node (confirmed in current workflow). The callback_data format `action:container` allows parsing:
|
||||
**Source:** [Unraid API generated-schema.graphql](https://raw.githubusercontent.com/unraid/api/main/api/generated-schema.graphql)
|
||||
|
||||
```javascript
|
||||
// In Code node
|
||||
const callbackData = $json.callback_query.data;
|
||||
const [action, container] = callbackData.split(':');
|
||||
return { action, container };
|
||||
### 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
|
||||
```
|
||||
|
||||
**Answer Callback Query (remove loading spinner):**
|
||||
---
|
||||
|
||||
## 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
|
||||
// HTTP Request node
|
||||
// URL: https://api.telegram.org/bot{{ $credentials.telegramApi.token }}/answerCallbackQuery
|
||||
// Method: POST
|
||||
{
|
||||
"callback_query_id": "={{ $json.callback_query.id }}",
|
||||
"text": "Processing..."
|
||||
"container1": true,
|
||||
"container2": false,
|
||||
"container3": "undef"
|
||||
}
|
||||
```
|
||||
|
||||
### Callback Data Limits
|
||||
**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
|
||||
|
||||
Telegram limits `callback_data` to 64 bytes. Use short encodings:
|
||||
- `s:plex` instead of `start:plex`
|
||||
- Single char actions: s=start, t=stop, r=restart, u=update, l=logs
|
||||
**Confidence:** MEDIUM (structure inferred from forum posts, confirmed in source code)
|
||||
|
||||
### Alternative: Custom Community Node
|
||||
**Source:** [Unraid Forums — Docker Update Status](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/page/9/)
|
||||
|
||||
If HTTP Request becomes unwieldy, install:
|
||||
```
|
||||
Settings > Community Nodes > Install
|
||||
Package: @topvisor/n8n-nodes-telegram-send-message-custom
|
||||
```
|
||||
### `/boot/config/plugins/dockerMan/templates-user/`
|
||||
|
||||
This allows passing any Telegram API parameters as JSON, including `reply_markup`.
|
||||
**Contains:** XML templates with container configuration, including `<Date>` and `<DateInstalled>` fields (internal, auto-managed by Unraid).
|
||||
|
||||
**Confidence:** MEDIUM - HTTP Request approach confirmed working by multiple community members. Native node limitations confirmed by open issues and PR #17258.
|
||||
|
||||
**Sources:**
|
||||
- [n8n Telegram Callback Operations](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.telegram/callback-operations/)
|
||||
- [PR #17258: JSON Keyboard Support](https://github.com/n8n-io/n8n/pull/17258)
|
||||
- [Custom Telegram Node](https://github.com/topvisor/n8n-nodes-telegram-send-message-custom)
|
||||
- [Telegram Bot API - Inline Keyboards](https://core.telegram.org/bots/api#inlinekeyboardmarkup)
|
||||
**Why not modify:** Templates are for configuration, not runtime status. Unraid ignores template dates for update checking (uses image digests instead).
|
||||
|
||||
---
|
||||
|
||||
## Unraid Integration
|
||||
## Network Configuration
|
||||
|
||||
### How Unraid Detects Updates
|
||||
### Docker Network Modes in Unraid
|
||||
|
||||
Unraid checks for Docker image updates by comparing local image digests against remote registry digests:
|
||||
| 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 |
|
||||
|
||||
1. Local image has digest (e.g., `35049b54ac64`)
|
||||
2. Unraid queries registry for `latest` tag digest (e.g., `96c1da19c304`)
|
||||
3. If different, shows "Update Available" badge
|
||||
**n8n container likely uses:** Bridge mode (most common for Unraid containers)
|
||||
|
||||
Update status is stored in: `/var/lib/docker/unraid-update-status.json`
|
||||
|
||||
### The Problem
|
||||
|
||||
When containers are updated externally (Watchtower, Portainer, or our bot), Unraid doesn't detect the change:
|
||||
- Container updates successfully
|
||||
- Unraid still shows "Update Available" badge
|
||||
- Manual "Check for Updates" doesn't fix it
|
||||
- Only deleting `unraid-update-status.json` and re-checking clears it
|
||||
|
||||
### Root Cause
|
||||
|
||||
Unraid only checks for **newly available** updates, not for containers that are **no longer** out of date. This is a known regression/limitation in Unraid's Docker management.
|
||||
|
||||
### Solution Options
|
||||
|
||||
| Option | Approach | Reliability |
|
||||
|--------|----------|-------------|
|
||||
| Delete status file | `rm /var/lib/docker/unraid-update-status.json` after update | HIGH - forces full recheck |
|
||||
| Trigger recheck | Unraid WebUI "Check for Updates" after file delete | MEDIUM - requires UI or API |
|
||||
| Accept mismatch | Document that badge may be stale | LOW - poor UX |
|
||||
|
||||
### Recommendation: Delete Status File + Document
|
||||
|
||||
**Approach:**
|
||||
1. After bot successfully updates a container, delete the status file
|
||||
2. Document that users should click "Check for Updates" in Unraid UI to refresh badges
|
||||
3. Future enhancement: investigate if Unraid has an API to trigger update check
|
||||
|
||||
**Implementation:**
|
||||
|
||||
Add to update workflow after successful container recreation:
|
||||
### Accessing Unraid API from n8n Container
|
||||
|
||||
**Option 1: host.docker.internal (recommended)**
|
||||
```bash
|
||||
# In Execute Command node (runs on Unraid host)
|
||||
rm -f /var/lib/docker/unraid-update-status.json
|
||||
# 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
|
||||
```
|
||||
|
||||
**Caveat:** This requires the n8n container to have access to `/var/lib/docker/` on the host, which is a security consideration. Alternative: document the manual step.
|
||||
**Option 2: Unraid hostname/IP**
|
||||
```
|
||||
URL: http://tower.local/graphql
|
||||
# or
|
||||
URL: http://192.168.1.100/graphql # Replace with actual Unraid IP
|
||||
```
|
||||
|
||||
### Integration Points
|
||||
**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
|
||||
```
|
||||
|
||||
| File/API | Purpose | Access Method |
|
||||
|----------|---------|---------------|
|
||||
| `/var/lib/docker/unraid-update-status.json` | Update badge status | Host filesystem |
|
||||
| Unraid WebUI | Trigger update check | Manual (no API found) |
|
||||
|
||||
**Confidence:** MEDIUM - File location and behavior confirmed by multiple Unraid forum threads. No official Unraid API documentation found for programmatic update checks.
|
||||
|
||||
**Sources:**
|
||||
- [Unraid Forum: Docker shows "update ready" after updating](https://forums.unraid.net/topic/157820-docker-shows-update-ready-after-updating/)
|
||||
- [Watchtower + Unraid Discussion](https://github.com/containrrr/watchtower/discussions/1389)
|
||||
- [Unraid Forum: Incorrect docker update notification](https://forums.unraid.net/bug-reports/stable-releases/regression-incorrect-docker-update-notification-r2807/)
|
||||
**Source:** [Docker host.docker.internal guide](https://eastondev.com/blog/en/posts/dev/20251217-docker-host-access/)
|
||||
|
||||
---
|
||||
|
||||
## Recommendations Summary
|
||||
## Security Considerations
|
||||
|
||||
### Stack Additions
|
||||
### API Key Management
|
||||
|
||||
| Component | Recommendation | Confidence |
|
||||
|-----------|----------------|------------|
|
||||
| n8n API | Use existing public API with API key auth | MEDIUM |
|
||||
| Docker security | LinuxServer socket-proxy | HIGH |
|
||||
| Telegram keyboards | HTTP Request node to Telegram API | MEDIUM |
|
||||
| Unraid sync | Delete status file after update | MEDIUM |
|
||||
**Storage:**
|
||||
- Add to `.env.unraid-api` (already gitignored)
|
||||
- Load in n8n workflow via environment variables
|
||||
|
||||
### Implementation Order
|
||||
**Permissions:**
|
||||
- Use **least privilege**: `DOCKER:UPDATE_ANY` only
|
||||
- Avoid `--roles ADMIN` (grants full access)
|
||||
|
||||
1. **Docker socket proxy** - Security improvement, low risk, well-documented
|
||||
2. **Telegram inline keyboards** - UX improvement, proven pattern
|
||||
3. **n8n API access** - Developer tooling, not user-facing
|
||||
4. **Unraid update sync** - Nice-to-have, requires additional host access
|
||||
**Rotation:**
|
||||
- API keys don't expire, but should be rotated periodically
|
||||
- Delete via CLI: `unraid-api apikey --delete <key-id>`
|
||||
|
||||
### No New Dependencies Required
|
||||
**Source:** [Unraid API Key Management](https://docs.unraid.net/API/programmatic-api-key-management/)
|
||||
|
||||
All solutions use existing n8n capabilities:
|
||||
- HTTP Request node (Telegram API, Docker via proxy)
|
||||
- Execute Command node (Unraid file deletion)
|
||||
- n8n public API (existing feature)
|
||||
### Network Exposure
|
||||
|
||||
The only new container is `socket-proxy`, which is infrastructure, not application code.
|
||||
**Unraid API runs on:**
|
||||
- HTTP (port 80) or HTTPS (port 443) on Unraid host
|
||||
- Same interface as WebGUI
|
||||
|
||||
### Key Risks
|
||||
**Risk:** n8n container can access full GraphQL API (not just Docker mutations)
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Socket proxy misconfiguration | Test each Docker operation after setup |
|
||||
| Telegram API token exposure | Use n8n credentials, never log full URLs |
|
||||
| Unraid status file access | May need additional volume mount or manual step |
|
||||
| n8n API key security | Store outside repository, rotate periodically |
|
||||
**Mitigation:** Use scoped API key (limits permissions to Docker operations only)
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
## Implementation Checklist
|
||||
|
||||
**Research date:** 2026-02-02
|
||||
**Valid until:** 2026-03-02 (30 days)
|
||||
- [ ] 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
|
||||
|
||||
**Confidence breakdown:**
|
||||
- n8n API: MEDIUM - Docs confirm existence, specific endpoints need validation
|
||||
- Docker socket proxy: HIGH - Official docs, multiple implementations
|
||||
- Telegram keyboards: MEDIUM - Community-confirmed workarounds
|
||||
- Unraid integration: MEDIUM - Forum-confirmed behavior, no official API
|
||||
---
|
||||
|
||||
**Gaps:**
|
||||
- n8n API specific endpoint paths should be validated via Swagger UI
|
||||
- Unraid may have undocumented API for update checks (not found)
|
||||
- PR #17258 merge timeline unknown
|
||||
## 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/)
|
||||
|
||||
Reference in New Issue
Block a user