diff --git a/ATHENA.md b/ATHENA.md
index 5da812b..224db31 100644
--- a/ATHENA.md
+++ b/ATHENA.md
@@ -31,10 +31,14 @@ werden keine zweiten Instanzen dieser Dienste angelegt.
| `/etc/mike-ai` | lokale Konfiguration und Secrets, niemals Git |
Portainer läuft als separater, optionaler Verwaltungscontainer
-`mike-ai-portainer`, teilt den Netzwerk-Namespace des WireGuard-Gateways und
-ist unter `https://192.168.1.212:9443` erreichbar. Seine Einstellungen liegen
-im Docker-Volume `portainer_data`, das vom Athena-Backup mitgesichert wird.
-Portainer beobachtet beziehungsweise
+`mike-ai-portainer` im internen Frontend-Netz und ist ausschließlich über den
+namensbasierten Proxy des WireGuard-Gateways unter
+`https://192.168.1.212:9443` erreichbar. Das Dashboard verwendet denselben
+stabilen Aufbau auf Port 8099. Beide teilen ausdrücklich nicht den
+Netzwerk-Namespace des Gateway-Containers: Ein Recreate des Gateways kann sie
+dadurch nicht mehr in einem veralteten Namespace zurücklassen. Portainers
+Einstellungen liegen im Docker-Volume `portainer_data`, das vom Athena-Backup
+mitgesichert wird. Portainer beobachtet beziehungsweise
verwaltet Docker, ist aber keine Abhängigkeit des Inferenz-Stacks.
## Standardbefehle
diff --git a/compose.yaml b/compose.yaml
index 08535be..30cb741 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -50,11 +50,6 @@ services:
- /tmp:size=16m,mode=1777
volumes:
- "${WIREGUARD_CONFIG_FILE:-/etc/mike-ai/wireguard/fritz-athena.conf}:/run/secrets/fritz-athena.conf:ro"
- # Namespace-sharing services cannot publish ports themselves. The owner
- # must keep these bindings so a gateway recreation cannot hide their UIs.
- ports:
- - "8099:8099"
- - "9443:9443"
networks:
frontend:
ipv4_address: 172.30.10.254
@@ -843,7 +838,7 @@ services:
image: mike-ai/llama-dashboard:local
container_name: mike-ai-llama-dashboard
restart: unless-stopped
- network_mode: "service:wireguard-gateway"
+ networks: [frontend]
gpus: all
read_only: true
tmpfs:
@@ -884,7 +879,7 @@ services:
image: ${PORTAINER_IMAGE:-portainer/portainer-ce@sha256:511f3f06c96fe3b993ebeaafde311c1959cae73a7ef825dba6397d51b450dffa}
container_name: mike-ai-portainer
restart: unless-stopped
- network_mode: "service:wireguard-gateway"
+ networks: [frontend]
command: [--no-setup-token]
volumes:
- /var/run/docker.sock:/var/run/docker.sock
diff --git a/dev/test_recovery.py b/dev/test_recovery.py
index 9a529f7..2916185 100644
--- a/dev/test_recovery.py
+++ b/dev/test_recovery.py
@@ -82,17 +82,23 @@ class RecoveryScriptTests(unittest.TestCase):
for profile in ("fast", "medium", "large", "ultra", "uncensored"):
self.assertIn(f"llama-{profile}", installer)
- def test_gateway_consumers_are_stopped_before_gateway_recreation(self) -> None:
+ def test_gateway_consumers_do_not_require_rebinding(self) -> None:
manager = (ROOT / "manage.sh").read_text(encoding="utf-8")
- stop = 'stop llama-dashboard portainer'
- deploy = 'up -d --build'
- self.assertIn(stop, manager)
- self.assertLess(manager.index(stop), manager.index(deploy))
+ installer = (ROOT / "install.sh").read_text(encoding="utf-8")
+ self.assertNotIn('stop llama-dashboard portainer', manager)
+ self.assertNotIn('stop llama-dashboard portainer', installer)
+ self.assertNotIn('force-recreate llama-dashboard portainer', manager)
- def test_gateway_owns_shared_ui_ports_and_portainer_backup(self) -> None:
+ def test_gateway_proxies_stable_ui_services_and_portainer_backup(self) -> None:
compose = (ROOT / "compose.yaml").read_text(encoding="utf-8")
- self.assertIn('"8099:8099"', compose)
- self.assertIn('"9443:9443"', compose)
+ gateway = (ROOT / "platform/docker/wireguard-gateway/entrypoint.sh").read_text(
+ encoding="utf-8"
+ )
+ self.assertNotIn('network_mode: "service:wireguard-gateway"', compose)
+ self.assertNotIn('"8099:8099"', compose)
+ self.assertNotIn('"9443:9443"', compose)
+ self.assertIn('start_proxy 8099 llama-dashboard:8099', gateway)
+ self.assertIn('start_proxy 9443 portainer:9443', gateway)
self.assertIn('portainer-data:/backup/volumes/portainer-data:ro', compose)
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index cbf0ff7..0eb4ae6 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -19,10 +19,10 @@ flowchart LR
H --> X[Nginx-Proxy-Manager-MCP]
U --> M[Media-Tools
ffmpeg / ffprobe / yt-dlp]
- W[WireGuard-Gateway
Athena] --- R
- W --- B[Athena Dashboard :8099]
- W --- PRT[Portainer :9443
optionale Docker-Ansicht]
- W --- O[Athena Operator]
+ W[WireGuard-Gateway
Athena] -->|DNS-Proxy| R
+ W -->|DNS-Proxy :8099| B[Athena Dashboard
internes Frontend-Netz]
+ W -->|DNS-Proxy :9443| PRT[Portainer
internes Frontend-Netz]
+ W -->|DNS-Proxy| O[Athena Operator]
K[Backup alle 5 Stunden] --> DATA[/data und /etc/mike-ai]
```
@@ -34,6 +34,9 @@ flowchart LR
- **MUA** verwaltet Unraid. **Athena Operator** bleibt auf den Athena-Host
begrenzt.
- Der Router ist die einzige Modelladresse, die Hermes kennen muss.
+- Dashboard und Portainer besitzen eigene Netzwerk-Namespaces. Das
+ WireGuard-Gateway löst ihre stabilen Compose-Dienstnamen bei jeder
+ Verbindung neu auf; seine konkrete Container-ID ist damit irrelevant.
## Dynamische Qwen-Profile
diff --git a/install.sh b/install.sh
index 08da825..8ee955f 100755
--- a/install.sh
+++ b/install.sh
@@ -517,7 +517,6 @@ build_and_start() {
llama-fast llama-medium llama-beta1 llama-large llama-ultra llama-uncensored
docker compose --env-file "$SECRETS_DIR/stack.env" --profile image create image-worker
docker volume inspect portainer_data >/dev/null 2>&1 || docker volume create portainer_data >/dev/null
- docker compose --env-file "$SECRETS_DIR/stack.env" stop llama-dashboard portainer
docker compose --env-file "$SECRETS_DIR/stack.env" up -d --build \
wireguard-gateway qwen3-tts piper tts-gateway profile-controller router llama-dashboard portainer backup
if [[ ${WIREGUARD_MODE:-container} == container ]]; then
diff --git a/manage.sh b/manage.sh
index 1a368aa..f404812 100755
--- a/manage.sh
+++ b/manage.sh
@@ -51,24 +51,10 @@ case "$command" in
shift
[[ $# -eq 0 ]] || { echo "core akzeptiert keine weiteren Services" >&2; exit 2; }
run "$ROOT_DIR/platform/mcp/install-tools.sh"
- # Release the old gateway namespace (and its fixed network addresses)
- # before replacing its owner. Otherwise Docker can strand the host with
- # the old namespace still held by these two consumers.
- run "${compose[@]}" stop llama-dashboard portainer
run "${compose[@]}" up -d --build \
wireguard-gateway piper xtts tts-gateway profile-controller router llama-dashboard portainer backup
else
- rebind_gateway=false
- for service in "$@"; do
- [[ $service == wireguard-gateway ]] && rebind_gateway=true
- done
- if [[ $rebind_gateway == true ]]; then
- run "${compose[@]}" stop llama-dashboard portainer
- fi
run "${compose[@]}" up -d --build --no-deps "$@"
- if [[ $rebind_gateway == true ]]; then
- run "${compose[@]}" up -d --no-deps --force-recreate llama-dashboard portainer
- fi
fi
;;
purge-legacy)
diff --git a/platform/docker/wireguard-gateway/entrypoint.sh b/platform/docker/wireguard-gateway/entrypoint.sh
index 0dabbb5..dfc5fec 100644
--- a/platform/docker/wireguard-gateway/entrypoint.sh
+++ b/platform/docker/wireguard-gateway/entrypoint.sh
@@ -77,6 +77,8 @@ start_proxy 22 172.30.10.1:22
start_proxy 8081 router:8081
start_proxy 8085 tts-gateway:8085
start_proxy 8091 piper:8085
+start_proxy 8099 llama-dashboard:8099
start_proxy 8202 mcp-athena-operator:8000
+start_proxy 9443 portainer:9443
wait $(printf '%s\n' "$proxy_pids" | awk '{print $2}')
diff --git a/smoke-test.sh b/smoke-test.sh
index 49ce016..452b09d 100755
--- a/smoke-test.sh
+++ b/smoke-test.sh
@@ -37,6 +37,13 @@ for name in \
done
pass "Athena-Kerndienste sind gesund"
+for name in mike-ai-llama-dashboard mike-ai-portainer; do
+ network_mode=$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$name" 2>/dev/null || true)
+ [[ $network_mode != container:* ]] || \
+ fail "$name teilt noch einen fluechtigen Container-Netzwerk-Namespace"
+done
+pass "Dashboard und Portainer besitzen stabile Netzwerk-Namespaces"
+
active_llama=$(docker ps --format '{{.Names}}' | \
grep -Ec '^mike-ai-llama-(fast|medium|beta1|large|ultra|uncensored)$' || true)
[[ $active_llama -eq 1 ]] || fail "$active_llama aktive llama-Profile (erwartet: 1)"