Netgrimoire/Gremlin-Grimoire/Stack/Build-Config.md
2026-04-12 09:53:51 -05:00

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

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):

Gremlin with Badge

Badge / logo (green portal frame):

Gremlin Badge

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/Netgrimoire Forgejo repo. Gremlin commits audit reports to Netgrimoire/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.