Add deployments/ai-stack — combined production-shape example
Sanitized snapshot of the live srvno.de stack: Caddy + Ollama (with preseed) + ComfyUI + Open WebUI + Anubis stub. Real hostnames, secrets, and bcrypt hash replaced with placeholders so the dir is safe to commit. Caddyfile updated to point at comfyui:8188 (the source file pointed at the now-removed forge service). Dropped FIGMENT_/FORGE_/SEGMENT_IMAGE_TAG from the env example. Harmonised the init-models.sh mount path between ollama and model-init services. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,15 @@ Use `docker compose build` to rebuild locally when iterating on the Dockerfile.
|
||||
Then drop a checkpoint into the `comfyui-models` volume and finish the Open
|
||||
WebUI ComfyUI configuration. Full walkthrough in [SETUP.md](SETUP.md).
|
||||
|
||||
## Deployment examples
|
||||
|
||||
- **Repo root** (`docker-compose.yml`) — minimal `comfyui` + `open-webui`
|
||||
bridge for kicking the tyres on a single-purpose box.
|
||||
- **[`deployments/ai-stack/`](deployments/ai-stack/)** — production-shaped
|
||||
multi-service stack: Caddy (TLS + reverse proxy), Ollama (with one-shot
|
||||
model preseeding), ComfyUI, Open WebUI, optional Anubis anti-bot. Mirrors
|
||||
the live `srvno.de` deployment.
|
||||
|
||||
## Replaces
|
||||
|
||||
This repo supersedes the previous figment + segment + Forge stack. ComfyUI's
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copy to .env and fill in. docker compose picks .env up automatically.
|
||||
# Never commit the populated .env — it's in .gitignore at repo root.
|
||||
|
||||
# Cookie-signing key for Open WebUI sessions. Generate once with:
|
||||
# openssl rand -hex 32
|
||||
# Must stay stable across restarts — rotating it logs every user out.
|
||||
WEBUI_SECRET_KEY=replace-with-32-byte-hex
|
||||
|
||||
# Ed25519 private key (hex) for the Anubis PoW sidecar. Generate with:
|
||||
# openssl rand -hex 32
|
||||
# Only needed if you uncomment the anubis-owui service in docker-compose.yml.
|
||||
ANUBIS_OWUI_KEY=replace-with-32-byte-hex
|
||||
|
||||
# ComfyUI image tag to deploy. `latest` tracks whatever the release workflow
|
||||
# last pushed; pin to a v* tag (e.g. 0.1.0) for reproducible deploys.
|
||||
COMFYUI_IMAGE_TAG=latest
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
email you@example.com
|
||||
}
|
||||
|
||||
# ─── chat.example.com — Open WebUI ──────────────────────────────────────────
|
||||
# Swap `open-webui:8080` for `anubis-owui:8923` once the Anubis sidecar in
|
||||
# docker-compose.yml is uncommented.
|
||||
chat.example.com {
|
||||
reverse_proxy open-webui:8080
|
||||
}
|
||||
|
||||
# ─── comfyui.example.com — ComfyUI native web UI ────────────────────────────
|
||||
# Open WebUI submits jobs internally over the bridge — this hostname is only
|
||||
# for direct access to ComfyUI's own UI (workflow editing, custom-node
|
||||
# install via ComfyUI-Manager, etc.). Basic auth keeps it private; remove
|
||||
# the block if you don't need to expose it externally at all.
|
||||
comfyui.example.com {
|
||||
basic_auth {
|
||||
# Generate with:
|
||||
# docker run --rm caddy:latest caddy hash-password --plaintext 'your-password'
|
||||
comfyui REPLACE_WITH_BCRYPT_HASH
|
||||
}
|
||||
|
||||
reverse_proxy comfyui:8188
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# ai-stack — combined deployment example
|
||||
|
||||
The full multi-service stack: Caddy (TLS + reverse proxy) in front of Open
|
||||
WebUI (chat + image generation panel), Ollama (LLMs), and ComfyUI (image
|
||||
generation), with an optional Anubis PoW anti-bot sidecar. One GPU host,
|
||||
one bridge network, one TLS entry point.
|
||||
|
||||
This is what runs in production for the `srvno.de` deployment, sanitized
|
||||
for sharing.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------- | -------------------------------------------------------- |
|
||||
| `docker-compose.yml`| Service definitions, volumes, GPU reservations |
|
||||
| `Caddyfile` | TLS + reverse proxy config (one site block per hostname) |
|
||||
| `init-models.sh` | Models to preseed into Ollama on first boot |
|
||||
| `.env.example` | Secrets and image-tag pins. Copy to `.env` |
|
||||
|
||||
## Bring it up
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
# edit .env — generate the two keys with `openssl rand -hex 32`
|
||||
|
||||
# edit Caddyfile — replace example.com hostnames with yours, replace the
|
||||
# basic_auth bcrypt placeholder, point DNS at this host
|
||||
|
||||
# edit init-models.sh — keep the models you want, drop the rest
|
||||
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
First boot: Caddy provisions Let's Encrypt certs (DNS must already point
|
||||
here), the model-init container pulls the LLMs listed in `init-models.sh`
|
||||
(can take a while — `mistral-nemo:12b` is ~7 GB), and ComfyUI initialises
|
||||
its volumes empty. Drop a checkpoint into the `comfyui-models` volume
|
||||
under `checkpoints/` before the first image generation — see the repo
|
||||
root [SETUP.md](../../SETUP.md) §4.
|
||||
|
||||
After Open WebUI is up, finish the ComfyUI integration via the admin
|
||||
panel — paste the four workflow JSONs from [`../../workflows/`](../../workflows/).
|
||||
See [SETUP.md §6](../../SETUP.md).
|
||||
|
||||
## What's different from the upstream `comfyui-nvidia` standalone compose
|
||||
|
||||
The standalone compose at the repo root is just `comfyui` + `open-webui`
|
||||
on a fresh bridge — meant for kicking the tyres or running ComfyUI on a
|
||||
single-purpose box. This deployment adds:
|
||||
|
||||
- **Caddy** with auto-TLS, so Open WebUI and ComfyUI are reachable on
|
||||
real hostnames over HTTPS instead of `localhost:3000`.
|
||||
- **Ollama** for chat models, with a one-shot `model-init` puller that
|
||||
preseeds whatever `init-models.sh` lists.
|
||||
- **Anubis** anti-bot stub for protected hostnames (commented in by
|
||||
default; uncomment + provide a key to activate).
|
||||
- No host ports published for ComfyUI or Open WebUI — Caddy is the only
|
||||
service bound to 80/443.
|
||||
@@ -0,0 +1,187 @@
|
||||
# ai-stack — combined deployment example: caddy + comfyui + ollama +
|
||||
# open-webui (+ anubis later). One GPU host, one bridge network, one TLS
|
||||
# entry point.
|
||||
#
|
||||
# This is the "full meal" deployment. The minimal "just ComfyUI" example
|
||||
# lives in the repo root (../../docker-compose.yml).
|
||||
|
||||
name: ai-stack
|
||||
|
||||
volumes:
|
||||
comfyui-models:
|
||||
comfyui-custom-nodes:
|
||||
comfyui-input:
|
||||
comfyui-output:
|
||||
comfyui-user:
|
||||
ollama-data:
|
||||
open-webui-data:
|
||||
caddy-data:
|
||||
caddy-config:
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Caddy — the only service exposed on 80/443. Terminates TLS (auto Let's
|
||||
# Encrypt), reverse-proxies to the in-compose services by name.
|
||||
# ---------------------------------------------------------------------------
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
container_name: caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "443:443/udp" # HTTP/3
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy-data:/data
|
||||
- caddy-config:/config
|
||||
depends_on:
|
||||
- open-webui
|
||||
- comfyui
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:2019/config/"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ollama — LLM daemon, GPU-backed.
|
||||
# ---------------------------------------------------------------------------
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: ollama
|
||||
restart: unless-stopped
|
||||
# 11434 only published if you want direct access from the VM host.
|
||||
# Services inside the stack reach it via http://ollama:11434.
|
||||
ports:
|
||||
- "11434:11434"
|
||||
volumes:
|
||||
- ollama-data:/root/.ollama
|
||||
environment:
|
||||
- OLLAMA_HOST=0.0.0.0:11434
|
||||
- OLLAMA_KEEP_ALIVE=30m
|
||||
- OLLAMA_MAX_LOADED_MODELS=2
|
||||
- OLLAMA_FLASH_ATTENTION=1
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "ollama list >/dev/null 2>&1 || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 20s
|
||||
|
||||
# One-shot model puller. Runs after ollama is healthy, pulls whatever
|
||||
# init-models.sh lists, exits. `restart: "no"` keeps it from looping.
|
||||
model-init:
|
||||
image: ollama/ollama:latest
|
||||
container_name: ollama-model-init
|
||||
depends_on:
|
||||
ollama:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ollama-data:/root/.ollama
|
||||
- ./init-models.sh:/init-models.sh:ro
|
||||
environment:
|
||||
- OLLAMA_HOST=ollama:11434
|
||||
entrypoint: ["/bin/sh", "/init-models.sh"]
|
||||
restart: "no"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ComfyUI — image generation (txt2img + img2img), GPU-backed.
|
||||
# No host port published — Caddy fronts it. Uncomment 8188 only for
|
||||
# VM-local debug or to use ComfyUI's native web UI directly.
|
||||
#
|
||||
# Replaces the figment + segment + Forge trio. Open WebUI talks to
|
||||
# /prompt directly; SAM2/Grounding-DINO masking lives in custom_nodes
|
||||
# (install via ComfyUI-Manager) instead of as a separate sidecar.
|
||||
# ---------------------------------------------------------------------------
|
||||
comfyui:
|
||||
image: git.anomalous.dev/alphacentri/comfyui-nvidia:${COMFYUI_IMAGE_TAG:-latest}
|
||||
pull_policy: always
|
||||
container_name: comfyui
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - "8188:8188"
|
||||
volumes:
|
||||
- comfyui-models:/opt/comfyui/models
|
||||
- comfyui-custom-nodes:/opt/comfyui/custom_nodes
|
||||
- comfyui-input:/opt/comfyui/input
|
||||
- comfyui-output:/opt/comfyui/output
|
||||
- comfyui-user:/opt/comfyui/user
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "http://127.0.0.1:8188/system_stats"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 120s
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Open WebUI — multi-user chat.
|
||||
# ---------------------------------------------------------------------------
|
||||
open-webui:
|
||||
image: ghcr.io/open-webui/open-webui:main
|
||||
container_name: open-webui
|
||||
restart: unless-stopped
|
||||
# ports: not published; Caddy fronts it
|
||||
environment:
|
||||
ENABLE_OPENAI_API: "false"
|
||||
WEBUI_AUTH: "true"
|
||||
ENABLE_SIGNUP: "false"
|
||||
DEFAULT_USER_ROLE: "pending"
|
||||
WEBUI_URL: "https://chat.example.com"
|
||||
WEBUI_SECRET_KEY: "${WEBUI_SECRET_KEY}"
|
||||
OLLAMA_BASE_URL: "http://ollama:11434"
|
||||
ENABLE_IMAGE_GENERATION: "true"
|
||||
IMAGE_GENERATION_ENGINE: "comfyui"
|
||||
COMFYUI_BASE_URL: "http://comfyui:8188"
|
||||
IMAGE_SIZE: "1024x1024"
|
||||
IMAGE_STEPS: "45"
|
||||
# Workflow JSON + node mappings are pasted into the admin panel
|
||||
# (Settings → Images). Source files live in this repo under
|
||||
# workflows/ — txt2img.json, txt2img.nodes.json, img2img.json,
|
||||
# img2img.nodes.json.
|
||||
volumes:
|
||||
- open-webui-data:/app/backend/data
|
||||
depends_on:
|
||||
ollama:
|
||||
condition: service_healthy
|
||||
comfyui:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "http://127.0.0.1:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Anubis — PoW anti-bot sidecar(s). One per protected hostname. To enable:
|
||||
# uncomment the block, generate a key with `openssl rand -hex 32`, and flip
|
||||
# the corresponding `reverse_proxy` target in Caddyfile from
|
||||
# `open-webui:8080` → `anubis-owui:8923`.
|
||||
# ---------------------------------------------------------------------------
|
||||
anubis-owui:
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
container_name: anubis-owui
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
BIND: ":8923"
|
||||
TARGET: "http://open-webui:8080"
|
||||
DIFFICULTY: "4" # SHA-256 leading zeros; 4 = ~1s client work
|
||||
COOKIE_DOMAIN: "chat.example.com"
|
||||
METRICS_BIND: ":9090"
|
||||
ED25519_PRIVATE_KEY_HEX: "${ANUBIS_OWUI_KEY}"
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
# Preseed Ollama with the models the stack should have available at startup.
|
||||
# Runs once via the model-init service (see docker-compose.yml). Safe to
|
||||
# re-run — `ollama pull` is a no-op for models already present.
|
||||
#
|
||||
# Add or remove pulls to taste. The host needs enough disk for everything
|
||||
# listed here; check sizes at https://ollama.com/library before adding.
|
||||
|
||||
set -e
|
||||
|
||||
ollama pull mistral-nemo:12b
|
||||
ollama pull llama3.2:3b
|
||||
Reference in New Issue
Block a user