608 KiB
| title | description | published | date | tags | editor | dateCreated |
|---|---|---|---|---|---|---|
| Gremlin — Build & Configuration | Complete build and configuration reference for the Gremlin AI stack on NetGrimoire | true | 2026-04-02T12:22:30.000Z | gremlin, ai, docker, swarm, n8n, ollama | markdown | 2026-04-02T12:22:30.000Z |
Gremlin — Build & Configuration
Gremlin is the self-hosted AI agent for NetGrimoire. This document covers the complete build, deployment, and configuration of the Gremlin stack as of the initial deployment checkpoint.
Overview
Gremlin runs entirely on local hardware via Ollama — no cloud APIs, no external dependencies. It provides an AI chat interface, automated infrastructure auditing, and alert triage through a four-service Docker Swarm stack.
| Component | Role |
|---|---|
| Ollama | Local LLM inference engine |
| Open WebUI | Chat interface and RAG pipeline |
| Qdrant | Vector database for document embeddings |
| n8n | Workflow automation and agent orchestration |
Host: docker4
Network: netgrimoire (external overlay)
Swarm manager: znas
Repo path: services/swarm/stack/Gremlin/
Branding Assets
The Gremlin mascot — whiskey glass, cigar, mischievous grin — is the official NetGrimoire Gremlin character. Two versions:
Full character (scene):
Badge / logo (green portal frame):
Store these at assets/gremlin/ in the Netgrimoire docs repo for use in future Wiki.js pages and artifacts.
Repository Structure
services/swarm/stack/Gremlin/
├── .env # Environment variables (secrets)
├── deploy.sh # Deploy script — sources .env, runs docker stack config + deploy
└── gremlin-stack.yml # Swarm stack definition
Environment Variables
All secrets and configuration live in .env. Fill in all values before deploying.
# Open WebUI
WEBUI_SECRET_KEY= # Random hex string — generate with: openssl rand -hex 32
# n8n
N8N_USER=admin
N8N_PASSWORD= # Strong password
# ntfy (self-hosted)
NTFY_URL=https://ntfy.netgrimoire.com
# Forgejo — read token (scope: repository:read)
FORGEJO_URL=https://git.netgrimoire.com
FORGEJO_TOKEN=
# Forgejo — docs repo write token (scope: repository:read, write, contents)
FORGEJO_DOCS_OWNER=traveler
FORGEJO_DOCS_REPO=Netgrimoire
FORGEJO_WRITE_TOKEN=
# Ollama models
OLLAMA_MODEL_GENERAL=llama3.2:3b
OLLAMA_MODEL_CODE=qwen2.5-coder:7b
Stack File
services:
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- /DockerVol/ollama:/root/.ollama
environment:
- OLLAMA_ORIGINS=*
networks:
- netgrimoire
deploy:
labels:
- homepage.group=Gremlin
- homepage.name=Ollama
- homepage.icon=ollama.png
- homepage.href=http://ollama.netgrimoire.com:11434
- homepage.description=Local LLM Runtime
- kuma.ollama.http.name=Ollama API
- kuma.ollama.http.url=http://ollama:11434/api/tags
placement:
constraints:
- node.hostname == docker4
open-webui:
image: ghcr.io/open-webui/open-webui:main
ports:
- "3000:8080"
volumes:
- /DockerVol/open-webui:/app/backend/data
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}
- ENABLE_RAG_WEB_SEARCH=true
- ENABLE_OLLAMA_API=true
- QDRANT_HOST=qdrant
- QDRANT_PORT=6333
networks:
- netgrimoire
deploy:
labels:
- homepage.group=Gremlin
- homepage.name=Open WebUI
- homepage.icon=openwebui.png
- homepage.href=https://ai.netgrimoire.com
- homepage.description=Gremlin Chat Interface
- kuma.openwebui.http.name=Open WebUI
- kuma.openwebui.http.url=http://open-webui:8080
- caddy=ai.netgrimoire.com
- caddy.reverse_proxy=open-webui:8080
- caddy_ingress_network=netgrimoire
placement:
constraints:
- node.hostname == docker4
qdrant:
image: qdrant/qdrant:latest
ports:
- "6333:6333"
- "6334:6334"
volumes:
- /DockerVol/qdrant:/qdrant/storage
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
networks:
- netgrimoire
deploy:
labels:
- homepage.group=Gremlin
- homepage.name=Qdrant
- homepage.icon=qdrant.png
- homepage.href=http://qdrant.netgrimoire.com:6333/dashboard
- homepage.description=Vector Database
- kuma.qdrant.http.name=Qdrant
- kuma.qdrant.http.url=http://qdrant:6333
placement:
constraints:
- node.hostname == docker4
n8n:
image: n8nio/n8n:latest
ports:
- "5678:5678"
volumes:
- /DockerVol/n8n:/home/node/.n8n
- /DockerVol/n8n/workflows:/home/node/.n8n/workflows
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
- WEBHOOK_URL=https://n8n.netgrimoire.com/
- GENERIC_TIMEZONE=America/Chicago
- N8N_EDITOR_BASE_URL=https://n8n.netgrimoire.com/
- OLLAMA_BASE_URL=http://ollama:11434
- NTFY_URL=${NTFY_URL}
- FORGEJO_URL=${FORGEJO_URL}
- FORGEJO_TOKEN=${FORGEJO_TOKEN}
- FORGEJO_DOCS_OWNER=${FORGEJO_DOCS_OWNER}
- FORGEJO_DOCS_REPO=${FORGEJO_DOCS_REPO}
- FORGEJO_WRITE_TOKEN=${FORGEJO_WRITE_TOKEN}
- N8N_BLOCK_ENV_ACCESS_IN_NODE=false
networks:
- netgrimoire
deploy:
labels:
- homepage.group=Gremlin
- homepage.name=n8n
- homepage.icon=n8n.png
- homepage.href=https://n8n.netgrimoire.com
- homepage.description=Workflow Automation
- kuma.n8n.http.name=n8n
- kuma.n8n.http.url=http://n8n:5678
- caddy=n8n.netgrimoire.com
- caddy.reverse_proxy=n8n:5678
- caddy_ingress_network=netgrimoire
placement:
constraints:
- node.hostname == docker4
networks:
netgrimoire:
external: true
Deploy Script
#!/bin/bash
set -euo pipefail
STACK_NAME="gremlin"
COMPOSE_FILE="gremlin-stack.yml"
ENV_FILE=".env"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "Loading environment..."
set -a && source "$ENV_FILE" && set +a
echo "Preprocessing stack file..."
docker stack config --compose-file "$COMPOSE_FILE" > resolved.yml
echo "Deploying $STACK_NAME..."
docker stack deploy --compose-file resolved.yml "$STACK_NAME"
rm -f resolved.yml
echo "Services:"
sleep 3
docker stack services "$STACK_NAME"
Volume Directories
Create these on docker4 before first deploy:
mkdir -p /DockerVol/ollama
mkdir -p /DockerVol/open-webui
mkdir -p /DockerVol/qdrant
mkdir -p /DockerVol/n8n/workflows
chown -R 1000:1000 /DockerVol/n8n
The chown on n8n is required — n8n runs as uid 1000 and will fail to start without write access to its data directory.
Model Setup
Run on docker4 after first deploy:
docker exec $(docker ps -qf name=gremlin_ollama) ollama pull llama3.2:3b
docker exec $(docker ps -qf name=gremlin_ollama) ollama pull qwen2.5-coder:7b
Verify:
docker exec $(docker ps -qf name=gremlin_ollama) ollama list
| Model | Size | Use |
|---|---|---|
| llama3.2:3b | ~2 GB | General Q&A, alert triage, summarization |
| qwen2.5-coder:7b | ~4.7 GB | Code analysis, compose audits, YAML generation |
| qwen2.5:14b | ~9 GB | Already present — deep reasoning tasks |
Known Issues & Fixes
n8n volume permission denied
Symptom: EACCES: permission denied, open '/home/node/.n8n/config'
Fix: chown -R 1000:1000 /DockerVol/n8n on docker4, then docker service update --force gremlin_n8n from znas.
Caddy label quoting
Symptom: caddy.reverse_proxy value has escaped quotes — "n8n:5678" instead of n8n:5678
Cause: docker stack config preprocessing adds quotes around {{upstreams N}} template values.
Fix: Use literal upstream address in labels — caddy.reverse_proxy=n8n:5678 — instead of the template helper.
Caddy wrong IP for upstream
Symptom: Caddy resolves service to wrong IP, 502 errors.
Fix: Add caddy_ingress_network=netgrimoire label to any service with Caddy labels. This pins Caddy to the correct network interface.
n8n env vars blocked in workflows
Symptom: access to env vars denied error in n8n workflow nodes.
Fix: Add N8N_BLOCK_ENV_ACCESS_IN_NODE=false to n8n environment in stack file.
Forgejo API 422 on file create (Forgejo 11+)
Symptom: [SHA]: Required when trying to create new files via API.
Cause: Forgejo 11 changed the contents API — PUT is for updates only, POST is required for new files.
Fix: Use POST to create, GET first to check for existing SHA, then PUT with SHA to update.
n8n Workflows
Forgejo Repo Audit
File: gremlin-forgejo-audit.json
Trigger: Schedule — Monday 06:00
Function: Fetches Caddyfile once, walks swarm/ and swarm/stack/*/ in the services repo, audits each YAML file against NetGrimoire standards using qwen2.5-coder:7b. Swarm files checked for homepage/kuma/caddy/placement labels. Compose files checked against Caddyfile for matching entries. FAILs trigger ntfy notification to gremlin-audits and commit a full report to Netgrimoire/Audits/ in the docs repo.
Architecture: All Forgejo and Ollama API calls are made inside Code nodes using this.helpers.httpRequest() — not HTTP Request nodes — to avoid n8n body expression limitations.
Uptime Kuma Alert Triage
File: gremlin-kuma-triage.json
Trigger: Webhook — https://n8n.netgrimoire.com/webhook/gremlin-kuma-alert
Function: Receives Uptime Kuma DOWN events, sends to llama3.2:3b for triage analysis (likely cause, immediate checks, severity), fires urgent ntfy notification to gremlin-alerts. RECOVERED events send a simple plain notification without AI analysis.
ntfy Topics
| Topic | URL | Events |
|---|---|---|
| gremlin-alerts | https://ntfy.netgrimoire.com/gremlin-alerts | Service DOWN triage, RECOVERED notices |
| gremlin-audits | https://ntfy.netgrimoire.com/gremlin-audits | Weekly audit FAILs, doc generation notices |
Service URLs
| Service | URL | Auth |
|---|---|---|
| Open WebUI | https://ai.netgrimoire.com | Local account |
| n8n | https://n8n.netgrimoire.com | Basic auth (see .env) |
| Ollama API | http://ollama.netgrimoire.com:11434 | None |
| Qdrant Dashboard | http://qdrant.netgrimoire.com:6333/dashboard | None |
Useful Commands
# Check all services (run on znas)
docker stack services gremlin
# View logs for a service
docker service logs gremlin_n8n --tail 50
docker service logs gremlin_ollama --tail 50
docker service logs gremlin_open-webui --tail 50
# Force restart a service
docker service update --force gremlin_n8n
# List loaded models (run on docker4)
docker exec $(docker ps -qf name=gremlin_ollama) ollama list
# Pull a new model
docker exec $(docker ps -qf name=gremlin_ollama) ollama pull <model>
Notes
- All services pinned to docker4 via placement constraints. Deploy commands run from znas as Swarm manager.
- Do not use
endpoint_mode: dnsrr— it breaks internal DNS resolution between services. Default VIP mode is required. - Wiki.js syncs from the
traveler/NetgrimoireForgejo repo. Gremlin commits audit reports toNetgrimoire/Audits/using POST for new files (Forgejo 11+ requirement). - n8n workflow imports: delete old workflow first, import JSON, click Publish. Each reimport resets node configurations — Code node approach avoids HTTP Request node body limitations.