Add OpenHands (agent-canvas) coding agent + Ollama backend
This commit is contained in:
23
README.md
23
README.md
@@ -24,8 +24,10 @@ This is a real, running homelab — not a demo. It's deployed on a home Ubuntu s
|
||||
| **Filebrowser** | `filebrowser/` | Web-based file manager for the server's storage |
|
||||
| **Media stack** | `mediastack/` | Plex (media server), Sonarr/Radarr/Prowlarr (media automation and indexing), qBittorrent (download client) |
|
||||
| **Minecraft** | `minecraft/` | Self-hosted game server |
|
||||
| **Sentinel** | `sentinel/` | Disk health monitoring integrated with smartctl dump the outputs in the container logs |
|
||||
| **Sentinel** | `sentinel/` | Disk health monitoring integrated with smartctl, dumps output to container logs |
|
||||
| **smartctl** | `smartctl/` | Disk health monitoring (S.M.A.R.T. data) |
|
||||
| **OpenHands** | `openhands/` | Self-hosted AI coding agent (OpenHands/agent-canvas) backed by Ollama — see [AI Coding Agent](docker-compose/docs/AI-Agent.md) |
|
||||
| **Nginx Proxy Manager** | `nginx-proxy-manager/` | Reverse proxy giving every service a friendly `.home` hostname with locally-trusted HTTPS — see [Reverse Proxy](docker-compose/docs/Reverse-Proxy.md) |
|
||||
|
||||
## Repository structure
|
||||
|
||||
@@ -59,6 +61,22 @@ homelab/
|
||||
│ └── docker-compose.yml
|
||||
├── smartctl/
|
||||
│ └── docker-compose.yml
|
||||
├── openhands/
|
||||
│ └── docker-compose.yaml
|
||||
├── nginx-proxy-manager/
|
||||
│ └── docker-compose.yaml
|
||||
├── docs/
|
||||
│ ├── Home.md
|
||||
│ ├── Architecture-and-Hardware.md
|
||||
│ ├── Monitoring-Stack.md
|
||||
│ ├── Network-Services.md
|
||||
│ ├── Media-Automation.md
|
||||
│ ├── Workflow-Automation.md
|
||||
│ ├── Storage-Health.md
|
||||
│ ├── AI-Agent.md
|
||||
│ ├── Reverse-Proxy.md
|
||||
│ ├── Lessons-Learned.md
|
||||
│ └── images/
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
@@ -76,12 +94,15 @@ docker compose up -d
|
||||
|
||||
- All `.env` files, runtime config directories, and databases are excluded via `.gitignore` — only deployment definitions (`docker-compose.yml`) and `.env.example` placeholders are tracked.
|
||||
- Passwords referenced in compose files are injected via environment variables, never hardcoded.
|
||||
- The AI coding agent (`openhands/`) currently uses a third-party API aggregator as its LLM backend — see [AI Coding Agent](docker-compose/docs/AI-Agent.md) for details and caveats. It has no access to the Docker socket or other containers on the host.
|
||||
|
||||
## Lessons learned
|
||||
|
||||
- Splitting each service into its own folder with its own Compose file made it much easier to reason about dependencies and update services independently, instead of one giant Compose file.
|
||||
- Runtime config/state (API keys, session tokens, library databases) needs to be gitignored at the directory level (`**/config/`, `**/data/`) — a plain text/secret grep alone isn't enough, since it silently skips over app-specific binary and XML config files.
|
||||
- Centralized monitoring (Prometheus + Grafana + cAdvisor + node-exporter) made it possible to actually see resource usage per container, which mattered a lot on lower-spec hardware.
|
||||
- Free-tier cloud AI APIs enforce regional access restrictions automatically — worth checking a provider's terms before building around "free tier" as an assumption. See [AI Coding Agent](docker-compose/docs/AI-Agent.md).
|
||||
- Wildcard TLS certs (`*.home`) are rejected by browsers on single-label local domains — certificates for local reverse proxies need explicit hostnames listed instead. See [Reverse Proxy](docker-compose/docs/Reverse-Proxy.md).
|
||||
|
||||
## Roadmap
|
||||
|
||||
|
||||
34
docs/AI-Agent.md
Normal file
34
docs/AI-Agent.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# AI Coding Agent
|
||||
|
||||
Self-hosted [OpenHands](https://github.com/OpenHands/OpenHands) (agent-canvas), backed by Ollama for local inference. Runs as its own Compose stack under `agent-canvas/`, isolated from the rest of the homelab.
|
||||
|
||||
## Stack
|
||||
|
||||
| Service | Role |
|
||||
|---|---|
|
||||
| agent-canvas | OpenHands web UI + agent runtime |
|
||||
| Ollama | Local model runtime, used as a fallback/experimentation backend |
|
||||
|
||||
## Finding a working LLM backend
|
||||
|
||||
Getting an actual model behind the agent turned out to be the hardest part of this setup — worth documenting since none of it was obvious going in.
|
||||
|
||||
**Free cloud APIs are geo-restricted.** OpenRouter, Google Gemini, and Groq were each tried in turn as a free backend. All three returned access-denied/region-restricted errors — each provider's terms of service explicitly excludes a number of sanctioned countries, and enforcement happens automatically based on request origin, not usage patterns. This ruled out the "just use a free-tier cloud API" approach entirely for this network.
|
||||
|
||||
**Fully local inference was tried, and works, but has a real limit.** Running a small model (`qwen2.5:0.5b`, later `qwen3:0.6b`) directly through Ollama on the homelab server itself was technically functional — but OpenHands' agent framework sends a large system prompt (tool definitions, agent instructions) with every single request, on the order of tens of thousands of tokens. On the server's older CPU, just processing that prompt (before generating any reply) took multiple minutes per message and occasionally timed out outright. This isn't a model-size problem — a bigger local model wouldn't fix it, since the bottleneck is prompt *prefill* speed on this hardware, not generation quality.
|
||||
|
||||
**Direct paid APIs (DeepSeek, Kimi/Moonshot) both require a funded account** with no meaningful free tier — a non-starter without a way to pay.
|
||||
|
||||

|
||||
|
||||
**Current working setup: [BazaarLink](https://bazaarlink.ai), an aggregator API.** It routes requests to underlying models (confirmed via its own usage dashboard: Gemini 2.5 Flash-Lite and DeepSeek V4 Flash) through its own infrastructure rather than directly from this network — which is what allows it to reach providers that would otherwise reject requests from this region. It currently has a working free/low-cost tier.
|
||||
|
||||
> **This is not a vetted recommendation.** BazaarLink is a small third-party service with no track record comparable to the major providers above. All prompts and any code the agent writes pass through its servers. It's being used here for low-stakes experimentation (small scripts, static pages) only — nothing involving real credentials, private repos, or sensitive data.
|
||||
|
||||
## Configuration
|
||||
|
||||
The active LLM profile in OpenHands' settings points at BazaarLink's OpenAI-compatible endpoint, using its `auto:free` routing keyword to let it pick whichever backend model is currently available on the free tier.
|
||||
|
||||
## Sandboxing
|
||||
|
||||
OpenHands runs with access only to its own mounted `./projects` folder — it does not have the Docker socket mounted, and cannot start, stop, or inspect other containers on the host. Given the model backend is an unverified third party, this boundary is intentional and hasn't been relaxed.
|
||||
BIN
docs/images/openhands-error.png
Normal file
BIN
docs/images/openhands-error.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 234 KiB |
23
openhands/compose.yaml
Normal file
23
openhands/compose.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
agent-canvas:
|
||||
image: ghcr.io/openhands/agent-canvas:latest
|
||||
container_name: agent-canvas
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- openhands-data:/home/openhands/.openhands
|
||||
- ./projects:/home/openhands/workspace/project
|
||||
stdin_open: true
|
||||
tty: true
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: ollama
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "11434:11434"
|
||||
volumes:
|
||||
- ollama-data:/root/.ollama
|
||||
volumes:
|
||||
openhands-data:
|
||||
ollama-data:
|
||||
Reference in New Issue
Block a user