Compare commits

...

84 Commits

Author SHA1 Message Date
1eacc3cd8e fix(paperclip): force Traefik to use ai_net network for routing
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
2026-05-18 22:13:59 -04:00
bce403232a Revert custom Dockerfile - not needed, adapter installs on persistent volume
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
The Hermes adapter can be installed once via Paperclip's adapter
management API and persists on the Docker volume across restarts.
No custom Dockerfile or build step required.
2026-05-18 18:38:17 -04:00
37bf43c3ea feat: add custom Dockerfile with Hermes adapter baked in
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
Creates ai/paperclip/ with:
- Dockerfile: extends upstream paperclip image, pre-installs
  hermes-paperclip-adapter@0.3.0 npm package as seed data
- docker-entrypoint.sh: seeds the adapter plugin on first boot
  if the persistent volume is empty, then runs original startup

This ensures the Hermes adapter is available on first boot without
requiring network access — no npm install needed at runtime. The
adapter persists on the Docker volume across restarts.
2026-05-18 18:37:31 -04:00
563ccc5632 feat: add Paperclip agent orchestrator to AI compose stack
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
Paperclip (ghcr.io/paperclipai/paperclip:v2026.517.0) is an open-source
agent management dashboard. Adds paperclip-db (PostgreSQL 17) and
paperclip services with Traefik reverse proxy on
paperclip.lazyworkhorse.net.

Requires .env: PAPERCLIP_DB_PASSWORD, PAPERCLIP_AUTH_SECRET.
2026-05-18 18:17:15 -04:00
8d1ae7e632 Remove the unsuported gitea action off 2026-05-13 13:11:11 -04:00
29ae32a1c5 Merge pull request 'fix: use ln -sf instead of update-alternatives --set for iptables-nft' (#28) from fix/vpn-iptables-nft-v3 into master
Reviewed-on: #28
2026-05-13 16:59:50 +00:00
8dff094768 fix: use ln -sf instead of update-alternatives --set
update-alternatives --set fails because the base image only registers
iptables-legacy as an alternative. The iptables-nft binary (/usr/sbin/iptables-nft)
exists but isn't in the alternatives database. Direct ln -sf bypasses this.
2026-05-13 12:58:43 -04:00
ec08f5eb5d Merge pull request 'fix: remove apk add iptables-nft — built-in on Alpine 3.18+' (#27) from fix/vpn-iptables-nft-v2 into master
Reviewed-on: #27
2026-05-13 16:49:23 +00:00
611e96b306 fix: remove apk add iptables-nft — built-in on Alpine 3.18+
In Alpine 3.18+, the 'iptables' package IS the nftables variant.
iptables-nft is not a separate package. The binary is already in
the base image — only need to flip update-alternatives.
2026-05-13 12:48:51 -04:00
f184ed957c Merge pull request 'fix: update wg-easy to official ghcr image with iptables-nft' (#26) from fix/vpn-iptables-nft-upstream into master
Reviewed-on: #26
2026-05-13 16:37:35 +00:00
2bf31c7ccc fix: update wg-easy to official ghcr image with iptables-nft
- Switch FROM weejewel/wg-easy:latest (4yr old, Alpine 3.11) to
  ghcr.io/wg-easy/wg-easy:latest (actively maintained, Alpine krypton)
- Use update-alternatives instead of raw ln -sf to flip iptables
  from legacy to nftables backend
- Fix compose build context: ./vpn -> . (Dockerfile was at same level)

The weejewel/wg-easy image lacked iptables-nft package in Alpine 3.11.
The new official image has it available, we just flip the alternatives.
The old ln -sf approach was fragile across Alpine versions.
2026-05-13 12:30:15 -04:00
f44f93e35a Merge pull request 'fix: add Himalaya email CLI to Hermes Docker image' (#25) from fix/himalaya-email-cli into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Reviewed-on: #25
2026-05-13 15:03:40 +00:00
4cdd157e3f Merge pull request 'fix: add iptables-nft to wg-easy for nftables-only kernels' (#24) from fix/wg-easy-iptables-nft into master
Reviewed-on: #24
2026-05-13 15:03:25 +00:00
3ba0345887 Merge pull request 'feat: install custom Hermes tools at startup, remove deprecated fix-permissions.sh' (#23) from feat/hermes-custom-tools-startup into master
Some checks failed
Build Hermes agent / build (push) Failing after 2s
Build ollama (gfx906) / build (push) Failing after 2s
Reviewed-on: #23
2026-05-13 13:52:36 +00:00
27571ddb3f feat: add Himalaya email CLI to Hermes Docker image
Some checks failed
Build Hermes agent / build (pull_request) Failing after 2s
2026-05-12 18:09:40 -04:00
5e242eb946 fix: add iptables-nft to wg-easy for nftables-only kernels
wg-easy's Alpine wg-quick uses legacy iptables which requires the
iptable_nat kernel module. On NixOS kernels compiled without legacy
netfilter modules, the container crashes in a restart loop:

  iptables v1.8.3 (legacy): can't initialize iptables table 'nat'
  Table does not exist (do you need to insmod?)

Fix: build a custom image that installs Alpine's iptables-nft package
and symlinks iptables -> iptables-nft (nftables backend).
2026-05-12 14:52:33 -04:00
e607982b21 refactor: chown tools dir at build time instead of root at runtime
Some checks failed
Build Hermes agent / build (pull_request) Failing after 3s
Build ollama (gfx906) / build (pull_request) Failing after 2s
2026-05-12 14:47:34 -04:00
4627199217 feat: install custom tools at startup, remove deprecated fix-permissions.sh
Some checks failed
Build Hermes agent / build (pull_request) Failing after 41m55s
Build ollama (gfx906) / build (pull_request) Failing after 2s
2026-05-12 13:38:26 -04:00
3611d645db Merge pull request 'feat: add Gitea Actions CI workflows for Docker builds' (#21) from feat/gitea-actions into master
Reviewed-on: #21
2026-05-11 02:39:02 +00:00
99ac8b5dcd Merge pull request 'feat: add act_runner and enable Gitea Actions' (#22) from feat/act-runner into master
Reviewed-on: #22
2026-05-11 02:38:41 +00:00
b517a65393 fix: add nixos-builder label for NixOS CI builds 2026-05-10 22:15:44 -04:00
61a59dfac1 Merge pull request 'feat: update Hermes Dockerfile to build from forked source (gortium/hermes-agent)' (#20) from feat/hermes-fork-dockerfile into master
Reviewed-on: #20
2026-05-11 02:07:41 +00:00
144678354f feat: add Gitea Actions runner and enable Actions 2026-05-10 21:38:19 -04:00
34b1cb83a0 feat: add Gitea Actions workflows for ollama and hermes Docker builds 2026-05-10 21:33:10 -04:00
f8c2f864de Merge pull request 'refactor: split ai/ into hermes/ and ollama/ directories with gfx906 build' (#19) from feat/ollama-gfx906 into master
Reviewed-on: #19
2026-05-11 01:26:11 +00:00
46241d10c5 feat: update Hermes Dockerfile to build from forked source
- Switch Dockerfile to clone from gortium/hermes-agent (Gitea fork)
- Add SSH agent forwarding for private repo clone at build time
- Set CHROME_EXECUTABLE for Playwright Chromium
- Remove patch_tts_tool.py (Piper patch now in fork source)
- Enable Gitea Actions in versioncontrol compose
2026-05-10 17:55:17 -04:00
6b82a26c25 fix: add ldflags for version, remove privileged, enable flash attention 2026-05-10 10:07:25 -04:00
9cc7edfb39 fix: set CMAKE_INSTALL_PREFIX=/build/dist at configure time for CPU, match preset for HIP 2026-05-10 00:44:56 -04:00
f31ae59717 fix: copy /build/dist/lib/ollama/ (not /build/dist/lib/) to avoid extra nesting 2026-05-10 00:10:39 -04:00
bf2f17c5e2 fix: use cmake -B to override preset binaryDir, cmake --build/--install use explicit path 2026-05-09 23:52:46 -04:00
208bfd4612 fix: pre-set CMAKE_HIP_COMPILER="" for CPU build to prevent HIP detection 2026-05-09 23:50:26 -04:00
32df546550 fix: use ROCm 6 preset with HIP language detection for proper GPU kernel compilation
- Use --preset 'ROCm 6' for HIP build step (enables enable_language(HIP))
- Remove /opt/rocm from PATH for CPU build to prevent check_language(HIP)
- Add CMAKE_PREFIX_PATH=/opt/rocm so find_package(hip) finds hip-config.cmake
- cmake --install --component HIP now works correctly with OLLAMA_RUNNER_DIR=rocm
2026-05-09 23:49:08 -04:00
0f7b22c19b fix: add /usr/local/go/bin to ROCm PATH (was overridden)
ENV PATH for ROCm overwrote the previous PATH that included Go.
Without Go in PATH, 'go build' fails with 'go: not found'.
2026-05-09 23:15:26 -04:00
fc777e2de2 fix: target only gfx906 for HIP compilation
gfx940/gfx1010/gfx1030/gfx1100 cause C++ narrowing errors in ollama's
mma.cuh with hipcc. Since we only have MI50 (gfx906) cards, compile
for gfx906 only. Reduces build time and avoids upstream code bugs.
2026-05-09 23:07:39 -04:00
d52f18b0fa fix: remove gfx1200 target (not supported by ROCm 6.1 clang 17)
ROCm 6.1's AMD clang 17 doesn't recognize gfx1200 architecture
(introduced in ROCm 6.2+). Caused compilation failure on all .cu files.
2026-05-09 22:53:11 -04:00
0d87fb2556 fix: build CPU and HIP backends separately
CPU backends compiled with GCC (fixes AVX intrinsic errors from hipcc).
HIP backend compiled with hipcc (legacy mode skips enable_language(HIP)).
Go binary built with GCC for CGo linking.
This avoids both CMAKE_HIP_COMPILER rejection and CXX=hipcc CPU failures.
2026-05-09 22:51:13 -04:00
f6bc2b07a7 fix: remove nonexistent CC=clang for Go build step
ROCm 6.1 image doesn't have clang/clang++ in PATH (only amdclang++).
GCC is the default and works fine for CGo linking.
2026-05-09 22:41:18 -04:00
aa6bbe87bf fix: correct AMDGPU_TARGETS to include gfx940/gfx1010/gfx1200
Targets were corrupted during previous patch iterations, contained
gfx908/gfx90a from the CMake preset instead of gfx940/gfx1010/gfx1200.
2026-05-09 22:40:40 -04:00
0c612d9731 fix: remove unsupported AMDGPU_TARGETS (gfx1200) for ROCm 6.1
ROCm 6.1's AMD clang 17 doesn't support gfx1200 (RDNA4).
Use only targets supported by ROCm 6.1: gfx906, gfx908, gfx90a, gfx1030, gfx1100.
2026-05-09 22:30:21 -04:00
5b210fe624 fix: use ROCm amdclang++ as HIP compiler, keep GCC for CPU code
Setting CXX=hipcc caused compilation failures on CPU backends (AVX
intrinsics). Now using GCC for CPU, ROCm's amdclang++ for HIP only.
2026-05-09 22:29:10 -04:00
d8b77c97c3 fix: use CXX=hipcc legacy mode for HIP CMake build
CMake 3.31 refuses CMAKE_HIP_COMPILER=hipcc with 'not supported'.
Using CXX=hipcc triggers the legacy HIP detection path which works.
2026-05-09 22:20:44 -04:00
a3d0fa0072 fix: set CMAKE_HIP_COMPILER explicitly for ROCm 6.1 HIP detection 2026-05-09 22:19:50 -04:00
956d76f14d fix: add unzip dependency for ninja installation 2026-05-09 22:14:53 -04:00
c6d2f5918f fix: use ollama v0.23.2 native CMake build system for ROCm 6 + gfx906
The old Dockerfile used the deprecated llama.cpp/ subdirectory approach
which doesn't exist in ollama v0.23.2. Now using the official CMake
presets (ROCm 6 preset) with AMDGPU_TARGETS including gfx906:xnack-.
2026-05-09 22:13:47 -04:00
f023dc1ee4 fix: update ollama Dockerfile to v0.23.2 with proper ROCm 6.1 + gfx906 build
- Update OLLAMA_VERSION from v0.13.5 to v0.23.2
- Fix package: golang -> golang-go
- Add ENV HCC_AMDGPU_TARGET=gfx906 and HSA_ENABLE_SDMA=0
- Set proper ENTRYPOINT + CMD
2026-05-09 21:56:14 -04:00
d34a4d3647 refactor: move hermes files into ai/hermes/ subdirectory
- ai/Dockerfile -> ai/hermes/Dockerfile
- ai/fix-permissions.sh -> ai/hermes/fix-permissions.sh
- ai/patch_tts_tool.py -> ai/hermes/patch_tts_tool.py
- ai/compose.yml: update hermes build context to ./hermes
- ollama stays at ai/ollama/Dockerfile
2026-05-09 21:50:04 -04:00
ef58155897 feat: add custom ollama image with ROCm 6.1 + gfx906 support
- Add ollama/Dockerfile that builds ollama from source with AMDGPU_TARGETS=gfx906
- Uses ROCm 6.1 (rocm/dev-ubuntu-22.04:6.1.2-complete) for MI50 support
- Builds llama.cpp runner with HIPBLAS for gfx906 architecture
- Updates compose.yml to build from this Dockerfile instead of pulling ollama/ollama:latest
2026-05-09 21:18:37 -04:00
0580603f27 Merge pull request 'fix: add TZ=America/Montreal for correct cron scheduling' (#18) from fix/hermes-timezone-v2 into master
Reviewed-on: #18
2026-05-09 19:54:52 +00:00
93c01fa314 fix: add TZ=America/Montreal for correct cron scheduling 2026-05-09 19:49:54 +00:00
6e540635bf Merge pull request 'feat(hermes): Piper TTS (local US male, no cloud)' (#17) from feat/voice-support-v2 into master
Reviewed-on: #17
2026-05-09 19:39:11 +00:00
Thierry Pouplier
b89be7b8f4 chore: restore system packages lost in base image migration
The migration from debian:stable-slim to nousresearch/hermes-agent:latest
dropped several packages that were previously installed. This restores:

- poppler-utils, imagemagick (PDF/image processing)
- texlive-latex-base, latex-extra, fonts-recommended, xetex, science
- qemu-user-static, binfmt-support (cross-compilation)
- emacs-nox (text editing)

These were added in PRs 3/5, 4/5, 5/5 and earlier commits of the
compose repo. The official image already has git, curl, ffmpeg,
python3, gcc, openssh, ripgrep, tini, docker-cli, etc.
2026-05-09 19:18:16 +00:00
Thierry Pouplier
748b5037b9 fix: update TTS patch for latest hermes-agent tts_tool.py
- Patch now matches the current tts_tool.py (newer version ships in
  nousresearch/hermes-agent:latest with different Edge fallback text)
- Adds dedicated elif provider == 'piper' block before else:
- Replaces else: fallback to use Piper instead of Edge
- Patches ALL copies (venv site-packages + /opt/hermes/tools/)
- Removes Edge TTS entirely as default/provider
2026-05-09 19:03:10 +00:00
Thierry Pouplier
3f80744ebd fix: install piper-tts as root (venv is root-owned in base image)
The nousresearch/hermes-agent:latest image creates its venv
as root. Running 'uv pip install' as USER hermes fails with
Permission denied on the site-packages directory.

Fix: keep USER root while modifying the venv, then switch
back to USER hermes for runtime.
2026-05-09 17:47:30 +00:00
Thierry Pouplier
6f17743667 fix: install into existing venv instead of recreating it
The nousresearch/hermes-agent:latest base image already has a
venv with hermes-agent installed at /opt/hermes/.venv/.
Running 'uv venv' on top of it either fails or wipes the
existing install.

Fix: activate the existing venv first, then pip install into it.
2026-05-09 17:44:55 +00:00
Thierry Pouplier
98216d2872 refactor: use official Hermes Agent image as base, not debian:stable-slim
Starting from debian:stable-slim required re-installing everything
(Hermes source, Node.js, Playwright, etc.) which was redundant
and fragile. The official nousresearch/hermes-agent image already
has all that.

Now the Dockerfile:
- FROM nousresearch/hermes-agent:latest (has tts_tool.py, Playwright, etc.)
- Install Piper + voice model on top
- Patch tts_tool.py at build time (Edge fallback -> Piper)
- Runtime fallback in fix-permissions.sh for volume resilience

Cleaner, smaller Dockerfile, and the build-time patch can find
tts_tool.py because it's in the base image's venv.
2026-05-09 17:39:23 +00:00
Thierry Pouplier
a40e347dfa fix: install hermes-agent from pip so build-time TTS patch works
The Dockerfile starts from debian:stable-slim, not from the official
Hermes image. Without installing hermes-agent from pip, there is no
tools/tts_tool.py in the image at build time, so the patch script
crashes with FileNotFoundError.

Adding hermes-agent to uv pip install gives us tts_tool.py in the
venv site-packages, so the COPY+RUN patch step works cleanly.

Also keep the runtime fallback in fix-permissions.sh for cases where
the volume's site-packages differ from the image.
2026-05-09 17:37:32 +00:00
Thierry Pouplier
cfa2a898c3 fix: move TTS patch from build-time to runtime
The build-time COPY+RUN of patch_tts_tool.py failed because
the Dockerfile starts from debian:stable-slim and only copies
the ai/ build context — there's no tools/tts_tool.py in the
image at build time (Hermes is on the mounted data volume).

Move patching to fix-permissions.sh which runs at container
startup when the data volume is mounted, so tts_tool.py is
available via the venv site-packages.

Also make patch_tts_tool.py robust: searches multiple paths
for tts_tool.py, accepts path as argument, exits 0 instead
of 1 when file/pattern not found (build must not fail).
2026-05-09 17:36:26 +00:00
Thierry Pouplier
0609720b33 fix: reinstate tts_tool.py patch step in Dockerfile
Commit 8e9a75f removed the COPY+RUN of patch_tts_tool.py
because the build context was thought to be insufficient.
The build context is ai/ which contains both the Dockerfile
and patch_tts_tool.py, so COPY works fine.

Without this step the tts_tool.py silently falls through
to Edge TTS as its default provider even when
config.yaml says provider: piper, because 'piper' is not
a recognized provider in the unpatched code. This caused
the female Edge TTS voice (AriaNeural) instead of the
configured Ryan High male voice.
2026-05-09 17:13:01 +00:00
Thierry Pouplier
d97f1cb1e5 fix: add startup permission fix for data volume (chown critical dirs on boot) 2026-05-09 16:04:32 +00:00
Thierry Pouplier
1a1cfec80a fix: add atomic write permission fix (preserves file mode on os.replace) 2026-05-09 15:50:29 +00:00
Thierry Pouplier
90e227bc4e feat: switch back to Ryan high quality voice 2026-05-09 15:21:49 +00:00
Thierry Pouplier
8e9a75fe5c fix: remove patch step from Dockerfile (build context is just ai/) 2026-05-09 14:28:35 +00:00
Thierry Pouplier
3016d0da2c fix: patch source tts_tool.py path, not site-packages 2026-05-09 14:27:07 +00:00
Thierry Pouplier
b750d26d80 feat: switch to Norman voice (US male, medium) 2026-05-09 14:20:46 +00:00
Thierry Pouplier
0a9507de65 fix: add ca-certificates for HuggingFace download 2026-05-09 14:14:52 +00:00
Thierry Pouplier
b3fa424661 fix: correct COPY path for patch_tts_tool.py (build context is ai/) 2026-05-09 14:12:06 +00:00
Thierry Pouplier
77fe8133ae fix: Dockerfile heredoc for voice download instead of multi-line -c 2026-05-09 14:09:50 +00:00
Thierry Pouplier
3f080da35e fix: clean patch script - only target Edge, no Coqui references 2026-05-09 13:59:09 +00:00
Thierry Pouplier
78f499bde8 fix: use full OPENROUTER_API_KEY variable name 2026-05-09 13:55:38 +00:00
Thierry Pouplier
e779818e73 chore: remove pycache 2026-05-09 13:41:54 +00:00
Thierry Pouplier
25d7611043 fix: clean Dockerfile with Piper TTS, external patch script 2026-05-09 13:41:37 +00:00
Thierry Pouplier
28213eec5c fix: replace Coqui/ROCm with Piper TTS (simpler, local, CPU) 2026-05-09 13:24:08 +00:00
Thierry Pouplier
c2471818b2 feat: add ROCm + Coqui TTS with GPU support to Dockerfile 2026-05-09 04:09:57 +00:00
Thierry Pouplier
f5171a7d6e fix: replace Dockerfile with simplified stable-slim version 2026-05-09 02:38:30 +00:00
Thierry Pouplier
5c504501d3 feat: add ROCm GPU env vars to hermes service for faster-whisper STT 2026-05-09 00:20:57 +00:00
2fa481909f Merge pull request 'feat: add WireGuard VPN stack (wg-easy)' (#16) from feat/wireguard-vpn into master
Reviewed-on: #16
2026-05-09 00:11:56 +00:00
434b2835ff Merge remote-tracking branch 'origin/feat/wireguard-vpn' into HEAD 2026-05-04 23:05:09 -04:00
51cf83c420 Commeneted nomadnet for now. not usingit. 2026-05-04 23:01:58 -04:00
d9f62652cb Commented webui for now. now using it 2026-05-04 22:56:07 -04:00
Thierry Pouplier
bc49391b4f chore: clean up WireGuard from Hermes Dockerfile, keep custom build 2026-05-05 02:11:37 +00:00
Thierry Pouplier
acf45acdd9 feat: enable NET_ADMIN for Hermes container to support WireGuard 2026-05-05 01:48:21 +00:00
Thierry Pouplier
b021d0dba7 feat: add custom Hermes Dockerfile with WireGuard tools 2026-05-05 01:42:55 +00:00
1b0dbed52e Merge pull request 'feat: enable traefik access logs for fail2ban http jails' (#15) from feat/traefik-access-logs-for-fail2ban into master
- Reviewed-on: #15
- Tested on the host
2026-05-01 12:18:22 +00:00
Thierry Pouplier
a79fe9dffa feat: enable traefik access logs for fail2ban http jails 2026-05-01 03:06:14 +00:00
12 changed files with 658 additions and 38 deletions

View File

@@ -0,0 +1,31 @@
name: Build Hermes agent
on:
pull_request:
branches: [ master ]
paths:
- 'ai/hermes/**'
- 'ai/compose.yml'
push:
branches: [ master ]
paths:
- 'ai/hermes/**'
- 'ai/compose.yml'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
run: |
git clone -b "${{ github.head_ref || github.ref_name }}" \
https://gitea:${{ secrets.GITHUB_TOKEN }}@code.lazyworkhorse.net/gortium/compose.git .
git log --oneline -3
- name: Build hermes image
run: |
cd ai
docker compose build hermes 2>&1
- name: Verify image
run: |
docker run --rm ai-hermes /opt/hermes/.venv/bin/python --version 2>&1

View File

@@ -0,0 +1,31 @@
name: Build ollama (gfx906)
on:
pull_request:
branches: [ master ]
paths:
- 'ai/ollama/**'
- 'ai/compose.yml'
push:
branches: [ master ]
paths:
- 'ai/ollama/**'
- 'ai/compose.yml'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
run: |
git clone -b "${{ github.head_ref || github.ref_name }}" \
https://gitea:${{ secrets.GITHUB_TOKEN }}@code.lazyworkhorse.net/gortium/compose.git .
git log --oneline -3
- name: Build ollama image
run: |
cd ai
docker compose build ollama --no-cache 2>&1
- name: Verify version
run: |
docker run --rm ollama/ollama:rocm-gfx906 ollama --version 2>&1

View File

@@ -1,33 +1,39 @@
version: "3.8"
services:
webui:
image: ghcr.io/open-webui/open-webui:main
volumes:
- /mnt/HoardingCow_docker_data/Ollama/open-webui:/app/backend/data
restart: always
environment:
- OLLAMA_API_BASE_URL=http://ollama:11434/api
networks:
- ai_net
- ai_backend
labels:
- "traefik.enable=true"
# webui:
# image: ghcr.io/open-webui/open-webui:main
# volumes:
# - /mnt/HoardingCow_docker_data/Ollama/open-webui:/app/backend/data
# restart: always
# environment:
# - OLLAMA_API_BASE_URL=http://ollama:11434/api
# networks:
# - ai_net
# - ai_backend
# labels:
# - "traefik.enable=true"
# Router for HTTP + redirection to HTTPS
- "traefik.http.routers.webui-http.rule=Host(`ai.lazyworkhorse.net`)"
- "traefik.http.routers.webui-http.entrypoints=web"
- "traefik.http.routers.webui-http.middlewares=redirect-to-https"
# # Router for HTTP + redirection to HTTPS
# - "traefik.http.routers.webui-http.rule=Host(`ai.lazyworkhorse.net`)"
# - "traefik.http.routers.webui-http.entrypoints=web"
# - "traefik.http.routers.webui-http.middlewares=redirect-to-https"
# Router for HTTPS with TLS
- "traefik.http.routers.webui-https.rule=Host(`ai.lazyworkhorse.net`)"
- "traefik.http.routers.webui-https.entrypoints=websecure"
- "traefik.http.routers.webui-https.tls=true"
- "traefik.http.routers.webui-https.tls.certresolver=njalla"
# # Router for HTTPS with TLS
# - "traefik.http.routers.webui-https.rule=Host(`ai.lazyworkhorse.net`)"
# - "traefik.http.routers.webui-https.entrypoints=websecure"
# - "traefik.http.routers.webui-https.tls=true"
# - "traefik.http.routers.webui-https.tls.certresolver=njalla"
hermes:
image: nousresearch/hermes-agent:latest
build:
context: ./hermes
ssh:
- default
container_name: hermes
entrypoint: ["/bin/bash", "-c",
"bash /opt/data/hermes-tools/install.sh && exec /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh \"$@\"",
"hermes-entrypoint"]
restart: always
# Gateway run enables the internal API server on port 8642
command: gateway run
@@ -39,6 +45,13 @@ services:
- API_SERVER_KEY=hermes_local_key
- GATEWAY_ALLOW_ALL_USERS=true
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
# ROCm for GPU-accelerated faster-whisper STT
- HSA_OVERRIDE_GFX_VERSION=9.0.6
- HCC_AMDGPU_TARGET=gfx906
- HIP_VISIBLE_DEVICES=0,1
- ROCR_VISIBLE_DEVICES=0,1
- HSA_ENABLE_SDMA=0
- TZ=America/Montreal
volumes:
- /mnt/HoardingCow_docker_data/Hermes/data:/opt/data
devices:
@@ -51,9 +64,11 @@ services:
- ai_backend
ollama:
image: ollama/ollama:latest
build:
context: ./ollama
dockerfile: Dockerfile
image: ollama/ollama:rocm-gfx906
container_name: ollama
privileged: true
tty: true
restart: always
ports:
@@ -71,7 +86,7 @@ services:
- HSA_ENABLE_SDMA=0
- OLLAMA_HOST=0.0.0.0
- OLLAMA_DEBUG=1
- OLLAMA_FLASH_ATTENTION=0
- OLLAMA_FLASH_ATTENTION=1
- OLLAMA_NUM_PARALLEL=2
devices:
# Map the render nodes and KFD for ROCm to work inside the container
@@ -81,6 +96,62 @@ services:
- "303"
- "26"
paperclip-db:
image: postgres:17-alpine
container_name: paperclip-db
restart: always
environment:
POSTGRES_USER: paperclip
POSTGRES_PASSWORD: ${PAPERCLIP_DB_PASSWORD:?PAPERCLIP_DB_PASSWORD must be set}
POSTGRES_DB: paperclip
healthcheck:
test: ["CMD-SHELL", "pg_isready -U paperclip -d paperclip"]
interval: 5s
timeout: 5s
retries: 10
volumes:
- /mnt/HoardingCow_docker_data/Paperclip/pgdata:/var/lib/postgresql/data
networks:
- ai_backend
paperclip:
image: ghcr.io/paperclipai/paperclip:v2026.517.0
container_name: paperclip
restart: always
ports:
- "127.0.0.1:3100:3100"
environment:
- HOST=0.0.0.0
- PORT=3100
- SERVE_UI=true
- DATABASE_URL=postgres://paperclip:${PAPERCLIP_DB_PASSWORD}@paperclip-db:5432/paperclip
- BETTER_AUTH_SECRET=${PAPERCLIP_AUTH_SECRET:?PAPERCLIP_AUTH_SECRET must be set}
- PAPERCLIP_PUBLIC_URL=https://paperclip.lazyworkhorse.net
- PAPERCLIP_DEPLOYMENT_MODE=authenticated
- PAPERCLIP_DEPLOYMENT_EXPOSURE=private
volumes:
- /mnt/HoardingCow_docker_data/Paperclip/data:/paperclip
depends_on:
paperclip-db:
condition: service_healthy
networks:
- ai_net
- ai_backend
labels:
- "traefik.enable=true"
- "traefik.docker.network=ai_net"
- "traefik.http.routers.paperclip-http.rule=Host(`paperclip.lazyworkhorse.net`)"
- "traefik.http.routers.paperclip-http.entrypoints=web"
- "traefik.http.routers.paperclip-http.middlewares=redirect-to-https"
- "traefik.http.routers.paperclip-https.rule=Host(`paperclip.lazyworkhorse.net`)"
- "traefik.http.routers.paperclip-https.entrypoints=websecure"
- "traefik.http.routers.paperclip-https.tls=true"
- "traefik.http.routers.paperclip-https.tls.certresolver=njalla"
- "traefik.http.services.paperclip.loadbalancer.server.port=3100"
networks:
ai_net:
external: true

93
ai/hermes/Dockerfile Normal file
View File

@@ -0,0 +1,93 @@
# syntax=docker/dockerfile:1
# Hermes Agent -- custom fork build
# Builds on top of official image + overlays our forked source from Gitea.
# Requires Docker BuildKit. Pass SSH agent for git clone:
# docker compose build hermes
# Or manually:
# DOCKER_BUILDKIT=1 docker build --ssh default -t hermes-agent:custom .
# ---------- Base: official Hermes image (system deps, npm, uv, Playwright) ----------
FROM nousresearch/hermes-agent:latest
# ---------- Overlay our forked source ----------
# Uses SSH agent forwarding from the build host (no key baked into image).
# --exclude node_modules/.venv keeps the base image's pre-built layers intact.
# Only the Python source, web UI source, and config change.
RUN --mount=type=ssh \
mkdir -p /root/.ssh && \
ssh-keyscan -p 2222 code.lazyworkhorse.net >> /root/.ssh/known_hosts 2>/dev/null && \
cd /tmp && \
GIT_SSH_COMMAND='ssh -p 2222 -o StrictHostKeyChecking=no' \
git clone --depth 1 --branch main \
git@code.lazyworkhorse.net:gortium/hermes-agent.git fork && \
rsync -a --delete fork/ /opt/hermes/ \
--exclude node_modules \
--exclude .venv \
--exclude .git && \
rm -rf /tmp/fork /root/.ssh/
# ---------- Rebuild web UI ----------
# Source files changed; node_modules (from base image) reused.
RUN cd /opt/hermes && npm run build
# ---------- Reinstall Python package (editable) ----------
# Picks up source changes from our fork.
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir --no-deps -e /opt/hermes
# ---------- Extra system deps ----------
USER root
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libportaudio2 ca-certificates poppler-utils imagemagick \
texlive-latex-base texlive-latex-extra texlive-fonts-recommended \
texlive-xetex texlive-science \
qemu-user-static binfmt-support emacs-nox && \
rm -rf /var/lib/apt/lists/*
# ---------- UV ----------
COPY --chmod=0755 --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/
# ---------- Piper TTS ----------
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir piper-tts sounddevice numpy && \
mkdir -p /opt/hermes/.venv/share/piper/voices
RUN /opt/hermes/.venv/bin/python3 /dev/stdin << 'PYEOF'
import urllib.request
base = '/opt/hermes/.venv/share/piper/voices'
url = 'https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high/en_US-ryan-high.onnx'
urllib.request.urlretrieve(url, base + '/en_US-ryan-high.onnx')
urllib.request.urlretrieve(url + '.json', base + '/en_US-ryan-high.onnx.json')
PYEOF
# ---------- Install Himalaya email CLI ----------
RUN /opt/hermes/.venv/bin/python3 /dev/stdin << 'PYEOF'
import urllib.request, tarfile, os, shutil
url = 'https://github.com/pimalaya/himalaya/releases/download/v1.2.0/himalaya.x86_64-linux.tgz'
tgz = '/tmp/himalaya.tgz'
urllib.request.urlretrieve(url, tgz)
with tarfile.open(tgz) as t:
t.extractall('/tmp')
shutil.move('/tmp/himalaya', '/usr/local/bin/himalaya')
os.chmod('/usr/local/bin/himalaya', 0o755)
os.remove(tgz)
print('himalaya v1.2.0 installed')
PYEOF
# ---------- Install himalaya-ro wrapper ----------
COPY --chmod=0755 himalaya-ro.sh /usr/local/bin/himalaya-ro
# ---------- Runtime ----------
USER hermes
ENV HERMES_HOME=/opt/data
ENV PATH="/opt/data/.local/bin:${PATH}"
# Point browser tool to Playwright's Chromium (already in base image)
ENV CHROME_EXECUTABLE=/opt/hermes/.playwright/chromium/chrome-linux/chrome
# Ensure tools directory and toolsets.py are writable by the hermes runtime user
# so custom tools can be injected from the persistent volume at startup.
RUN chown -R hermes:hermes /opt/hermes/tools /opt/hermes/toolsets.py
VOLUME [ "/opt/data" ]

73
ai/hermes/himalaya-ro.sh Normal file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────
# himalaya-ro — Read-only wrapper for himalaya
#
# Blocks destructive commands and logs audit trail.
# Pass-through for read-only commands (list, read, search).
#
# Usage: himalaya-ro [options] <command> [args...]
#
# Install: place in PATH before the real himalaya, or use
# `ln -sf himalaya-ro /usr/local/bin/himalaya`
# ─────────────────────────────────────────────────────────────
set -o pipefail
# ── Configuration ───────────────────────────────────────────
HIMALAYA_BIN="${HIMALAYA_BIN:-/usr/local/bin/himalaya}"
AUDIT_LOG="${HIMALAYA_AUDIT_LOG:-/var/log/himalaya-audit.log}"
# ── Destructive commands we block ──────────────────────────
BLOCKED_CMDS=(
"message move"
"message delete"
"message copy"
"flag add"
"flag remove"
"folder create"
"folder delete"
"folder rename"
"template send"
"account configure"
"account delete"
)
# ── Determine the subcommand being invoked ─────────────────
# Strip leading options (--account, --output, etc.) to find the verb
ARGS=()
SKIP_NEXT=false
for arg in "$@"; do
if $SKIP_NEXT; then
SKIP_NEXT=false
continue
fi
if [[ "$arg" == --* ]]; then
case "$arg" in
--account|--output|--page|--page-size|--folder|--color|--format)
SKIP_NEXT=true ;;
esac
continue
fi
ARGS+=("$arg")
done
# Build subcommand string and check against blocklist
CMD_STR=""
for ((i=0; i<${#ARGS[@]}; i++)); do
if [ -z "$CMD_STR" ]; then
CMD_STR="${ARGS[$i]}"
else
CMD_STR="$CMD_STR ${ARGS[$i]}"
fi
for blocked in "${BLOCKED_CMDS[@]}"; do
if [[ "$CMD_STR" == "$blocked" ]]; then
TS=$(date '+%Y-%m-%d %H:%M:%S')
echo "[AUDIT] $TS BLOCKED: himalaya $*" >> "$AUDIT_LOG"
echo "ERROR: Command 'himalaya $CMD_STR ...' is blocked by read-only policy." >&2
echo " Audit log: $AUDIT_LOG" >&2
exit 100
fi
done
done
# ── Allow pass-through ─────────────────────────────────────
exec "$HIMALAYA_BIN" "$@"

181
ai/hermes/patch_tts_tool.py Normal file
View File

@@ -0,0 +1,181 @@
#!/usr/bin/env python3
"""Patch Hermes TTS tool: add Piper TTS provider, remove Edge TTS as default.
Patches ALL copies of tts_tool.py found (venv site-packages + /opt/hermes/tools/).
Searches multiple paths for tts_tool.py so it works both at build time
(in the image venv) and at runtime (on the mounted data volume).
Idempotent: if already patched, does nothing.
"""
import sys
import os
# ---------------------------------------------------------------------------
# Search for all copies of tts_tool.py
# ---------------------------------------------------------------------------
CANDIDATE_PATHS = [
"/opt/hermes/.venv/lib/python3.13/site-packages/tools/tts_tool.py",
"/opt/hermes/tools/tts_tool.py",
]
found_paths = []
for p in CANDIDATE_PATHS:
if os.path.exists(p):
found_paths.append(p)
print(f"Found tts_tool.py at: {p}")
# Also try to find via Python import
import subprocess
try:
result = subprocess.run(
[sys.executable, "-c", "import tools.tts_tool; print(tools.tts_tool.__file__)"],
capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
p = result.stdout.strip()
if os.path.exists(p) and p not in found_paths:
found_paths.append(p)
print(f"Found tts_tool.py via import at: {p}")
except Exception:
pass
if not found_paths:
print("WARNING: tts_tool.py not found anywhere. Patching deferred to runtime.")
print(f"Searched: {CANDIDATE_PATHS}")
sys.exit(0)
# ---------------------------------------------------------------------------
# Old else block: the Edge TTS default fallback to replace
# ---------------------------------------------------------------------------
old_else = ''' else:
# Default: Edge TTS (free), with NeuTTS as local fallback
edge_available = True
try:
_import_edge_tts()
except ImportError:
edge_available = False
if edge_available:
logger.info("Generating speech with Edge TTS...")
try:
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
pool.submit(
lambda: asyncio.run(_generate_edge_tts(text, file_str, tts_config))
).result(timeout=60)
except RuntimeError:
asyncio.run(_generate_edge_tts(text, file_str, tts_config))
elif _check_neutts_available():
logger.info("Edge TTS not available, falling back to NeuTTS (local)...")
provider = "neutts"
_generate_neutts(text, file_str, tts_config)
else:
return json.dumps({
"success": False,
"error": "No TTS provider available. Install edge-tts (pip install edge-tts) "
"or set up NeuTTS for local synthesis."
}, ensure_ascii=False)'''
# ---------------------------------------------------------------------------
# New block: elif provider == "piper" + else: fallback with Piper only
# ---------------------------------------------------------------------------
new_block = ''' elif provider == "piper":
# Piper TTS (local, CPU, no cloud, no Microsoft)
piper_binary = "/opt/hermes/.venv/bin/piper"
piper_config = tts_config.get("piper", {})
voice = piper_config.get("voice", "en_US-lessac-medium")
model_dir = piper_config.get("model_dir", "/opt/hermes/.venv/share/piper/voices")
model_path = os.path.join(model_dir, f"{voice}.onnx")
if not os.path.exists(model_path):
return json.dumps({
"success": False,
"error": "Piper TTS voice model not found. "
"Install Piper TTS and download a voice model."
}, ensure_ascii=False)
logger.info("Generating speech with Piper TTS (local, CPU)...")
import subprocess as _sp
cmd = [piper_binary, "--model", model_path, "--output-raw"]
try:
proc = _sp.Popen(cmd, stdin=_sp.PIPE, stdout=_sp.PIPE, stderr=_sp.PIPE)
raw_audio, stderr = proc.communicate(input=text.encode(), timeout=60)
if proc.returncode != 0:
raise RuntimeError(f"Piper TTS failed: {stderr.decode()[:200]}")
ffmpeg_cmd = ["ffmpeg", "-f", "s16le", "-ar", "22050", "-ac", "1", "-i", "-", "-y", file_str]
_sp.run(ffmpeg_cmd, input=raw_audio, capture_output=True, timeout=30)
except Exception as e:
return json.dumps({
"success": False,
"error": f"Piper TTS failed: {e}"
}, ensure_ascii=False)
else:
# Default: Piper TTS (local, CPU, no cloud, no Microsoft)
piper_binary = "/opt/hermes/.venv/bin/piper"
piper_config = tts_config.get("piper", {})
voice = piper_config.get("voice", "en_US-lessac-medium")
model_dir = piper_config.get("model_dir", "/opt/hermes/.venv/share/piper/voices")
model_path = os.path.join(model_dir, f"{voice}.onnx")
if os.path.exists(model_path) and os.path.exists(piper_binary):
logger.info("Generating speech with Piper TTS (local, CPU)...")
import subprocess as _sp
cmd = [piper_binary, "--model", model_path, "--output-raw"]
try:
proc = _sp.Popen(cmd, stdin=_sp.PIPE, stdout=_sp.PIPE, stderr=_sp.PIPE)
raw_audio, stderr = proc.communicate(input=text.encode(), timeout=60)
if proc.returncode != 0:
raise RuntimeError(stderr.decode()[:200])
ffmpeg_cmd = ["ffmpeg", "-f", "s16le", "-ar", "22050", "-ac", "1", "-i", "-", "-y", file_str]
_sp.run(ffmpeg_cmd, input=raw_audio, capture_output=True, timeout=30)
except Exception:
pass
else:
return json.dumps({
"success": False,
"error": "Piper TTS not available. Install piper-tts and download a voice model."
}, ensure_ascii=False)'''
# ---------------------------------------------------------------------------
# Apply the patch to all copies found
# ---------------------------------------------------------------------------
patched_any = False
for tts_path in found_paths:
with open(tts_path) as f:
code = f.read()
if 'provider == "piper"' in code:
print(f"ALREADY PATCHED: {tts_path}")
continue
if old_else in code:
code = code.replace(old_else, new_block, 1)
with open(tts_path, 'w') as f:
f.write(code)
print(f"PATCHED: {tts_path}")
patched_any = True
else:
print(f"SKIP {tts_path}: Edge fallback pattern not found")
import re
for m in re.finditer(r' else:\n # Default:', code):
start = max(0, m.start() - 100)
end = min(len(code), m.end() + 300)
print(f" Found 'else:/# Default:' at position {m.start()}:")
print(f" {code[start:end]}")
print(" ---")
# Don't exit with error — if one copy isn't patchable, try the others
if not patched_any:
all_patched = all(
'provider == "piper"' in open(p).read()
for p in found_paths
)
if all_patched:
print("All copies already patched.")
sys.exit(0)
print("WARNING: Could not patch any copy of tts_tool.py")
sys.exit(1)
print("tts_tool.py patched successfully across all copies.")

106
ai/ollama/Dockerfile Normal file
View File

@@ -0,0 +1,106 @@
# ollama-gfx906/Dockerfile
#
# Custom ollama image with ROCm 6.1 + gfx906 (MI50) support.
# The official ollama/rocm image ships ROCm 7.2 which dropped gfx906.
# This uses v0.23.2's native CMake build system with AMDGPU_TARGETS including gfx906.
#
# Build: docker build -t ollama/ollama:rocm-gfx906 ai/ollama
FROM rocm/dev-ubuntu-22.04:6.1.2-complete AS builder
# Build dependencies (CMake, Ninja, Go)
ARG CMAKEVERSION=3.31.2
ARG NINJAVERSION=1.12.1
ARG GOLANG_VERSION=1.22.0
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
curl git ccache build-essential pkg-config unzip \
&& rm -rf /var/lib/apt/lists/*
# Install CMake from official binaries
RUN curl -fsSL https://github.com/Kitware/CMake/releases/download/v${CMAKEVERSION}/cmake-${CMAKEVERSION}-linux-x86_64.tar.gz \
| tar xz -C /usr/local --strip-components 1
# Install Ninja
RUN curl -fsSL -o /tmp/ninja.zip \
https://github.com/ninja-build/ninja/releases/download/v${NINJAVERSION}/ninja-linux.zip \
&& unzip /tmp/ninja.zip -d /usr/local/bin && rm /tmp/ninja.zip
# Install Go
RUN curl -fsSL https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz \
| tar xz -C /usr/local
ENV PATH=/usr/local/go/bin:$PATH
ARG OLLAMA_VERSION=v0.23.2
RUN git clone --depth 1 --branch ${OLLAMA_VERSION} https://github.com/ollama/ollama.git /build
WORKDIR /build
# ROCm paths
ENV HIP_PATH=/opt/rocm
ENV ROCM_PATH=/opt/rocm
ENV CMAKE_GENERATOR=Ninja
ENV LDFLAGS=-s
# Step 1: Build CPU backends with GCC (no ROCm preset)
# Pre-set CMAKE_HIP_COMPILER="" to prevent check_language(HIP) from
# finding a HIP compiler (it searches /opt/rocm even without PATH).
# Remove /opt/rocm from PATH to prevent find_program from finding hipcc.
RUN mkdir -p build-cpu && \
PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
cmake -B build-cpu -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_HIP_COMPILER="" \
-DCMAKE_INSTALL_PREFIX=/build/dist && \
cmake --build build-cpu --target ggml-cpu -- -l $(nproc) && \
cmake --install build-cpu --component CPU --strip && \
echo "=== CPU install ===" && \
(find /build/dist/lib/ollama -type f -o -type l 2>&1 | head -20 || echo "empty")
# Step 2: Build HIP backend with ROCm preset + gfx906 target only
# The ROCm 6 preset enables HIP language detection (enable_language(HIP))
# which ensures GPU kernels are properly compiled for gfx906.
# OLLAMA_RUNNER_DIR=rocm from the preset, so HIP goes to lib/ollama/rocm/
# Need CMAKE_PREFIX_PATH so find_package(hip) finds hip-config.cmake
# at /opt/rocm/lib/cmake/hip/hip-config.cmake.
RUN mkdir -p build-hip && \
cmake -B build-hip \
--preset 'ROCm 6' \
-DAMDGPU_TARGETS="gfx906:xnack-" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="/opt/rocm" && \
cmake --build build-hip --target ggml-hip -- -l $(nproc) && \
cmake --install build-hip --component HIP --strip && \
echo "=== HIP install ===" && \
find /build/dist/lib/ollama -type f -o -type l | head -20
# Step 3: Build Go binary (GCC for CGo linking)
ENV CGO_ENABLED=1
RUN go build -trimpath -ldflags="-X=github.com/ollama/ollama/version.Version=${OLLAMA_VERSION}" -o /build/dist/ollama .
# ---------- Runtime image ----------
FROM ubuntu:24.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates curl libstdc++6 libgomp1 libvulkan1 libopenblas0 \
&& rm -rf /var/lib/apt/lists/*
# Copy ROCm 6.1 runtime libraries
# These are needed at runtime by ggml-hip via LD_LIBRARY_PATH
COPY --from=builder /opt/rocm/lib/ /opt/rocm/lib/
COPY --from=builder /opt/rocm/share/ /opt/rocm/share/
# Copy ollama binary + all backends (CPU + HIP)
# CPU install: /build/dist/lib/ollama/libggml-*.so
# HIP install: /build/dist/lib/ollama/rocm/libggml-hip.so
COPY --from=builder /build/dist/ollama /usr/bin/ollama
COPY --from=builder /build/dist/lib/ollama/ /usr/lib/ollama/
RUN ldconfig
ENV LD_LIBRARY_PATH=/opt/rocm/lib:/usr/lib/ollama/rocm:/usr/lib/ollama
ENV HSA_OVERRIDE_GFX_VERSION=9.0.6
ENV HCC_AMDGPU_TARGET=gfx906
ENV HSA_ENABLE_SDMA=0
EXPOSE 11434
ENTRYPOINT ["/bin/ollama"]
CMD ["serve"]

View File

@@ -1,15 +1,15 @@
version: "3.9"
services:
nomadnet:
image: ghcr.io/markqvist/nomadnet:master
container_name: nomadnet
restart: always
volumes:
- /mnt/HoardingCow_docker_data/Nomadnet:/root/.nomadnetwork
- /mnt/HoardingCow_docker_data/Reticulum:/root/.reticulum
# Reticulum transport must be reachable directly (NOT through Traefik)
ports:
- "4242:4242"
# nomadnet:
# image: ghcr.io/markqvist/nomadnet:master
# container_name: nomadnet
# restart: always
# volumes:
# - /mnt/HoardingCow_docker_data/Nomadnet:/root/.nomadnetwork
# - /mnt/HoardingCow_docker_data/Reticulum:/root/.reticulum
# # Reticulum transport must be reachable directly (NOT through Traefik)
# ports:
# - "4242:4242"
synapse:
image: ghcr.io/element-hq/synapse:latest

View File

@@ -13,17 +13,20 @@ services:
- "--certificatesresolvers.njalla.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.njalla.acme.httpchallenge.entrypoint=web"
- "--log.level=DEBUG"
- "--log.level=INFO"
- "--log.filepath=/var/log/traefik/traefik.log"
- "--accesslog.filepath=/var/log/traefik/access.log"
- "--providers.docker=true"
- "--providers.docker.exposedByDefault=false"
ports:
- "80:80"
- "443:443"
environment:
- NJALLA_TOKEN=${NJALLA_TOKEN}
- NJALLA_TOKEN=***
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /mnt/HoardingCow_docker_data/Traefik:/letsencrypt
- /var/log/traefik:/var/log/traefik
restart: unless-stopped
networks:
- traefik_backend

View File

@@ -7,8 +7,11 @@ services:
- USER_UID=1000
- USER_GID=1000
- GITEA__server__ROOT_URL=https://code.lazyworkhorse.net
- GITEA__actions__ENABLED=true
- SSH_PORT=2222
- SSH_LISTEN_PORT=2222
# Enable Gitea Actions (act_runner required on host)
- GITEA__actions__ENABLED=true
volumes:
- /mnt/HoardingCow_docker_data/Gitea:/data
networks:
@@ -40,6 +43,22 @@ services:
# Internal Routing
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
act_runner:
image: gitea/act_runner:latest
container_name: act_runner
environment:
- GITEA_INSTANCE_URL=https://code.lazyworkhorse.net
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN}
- GITEA_RUNNER_NAME=ai-host-runner
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://catthehacker/ubuntu:full-22.04,nixos-builder:docker://nixos/nix
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- vc_net
restart: always
depends_on:
- gitea
networks:
vc_net:
external: true

9
vpn/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
# Custom wg-easy with iptables-nft (nftables-backed iptables)
# Fixes crash-loop when host kernel lacks legacy iptable_nat module.
FROM ghcr.io/wg-easy/wg-easy:latest
# The upstream image registers only iptables-legacy with update-alternatives.
# iptables-nft binary exists but isn't registered as an alternative key.
# Override the alternatives-managed symlinks directly.
RUN ln -sf /usr/sbin/iptables-nft /usr/sbin/iptables && \
ln -sf /usr/sbin/ip6tables-nft /usr/sbin/ip6tables

View File

@@ -2,7 +2,10 @@ version: "3.8"
services:
wireguard:
image: weejewel/wg-easy:latest
build:
context: .
dockerfile: Dockerfile
image: wg-easy-iptables-nft:latest
container_name: wireguard
cap_add:
- NET_ADMIN