yau-plant-assistant/compose/langfuse-compose.yml
Claude 0594fac3fb Fix four defects in langfuse-compose found deploying Phase 2
Bind address, and the reason this matters most. Langfuse is a Next.js
standalone server: it binds to whatever $HOSTNAME resolves to, not to all
interfaces. The container is on two networks and its hostname resolved to the
proxy address, so it listened on proxy only - HTTP 200 from proxy, HTTP 000
from ai-internal. Caddy is on proxy, so the public URL worked perfectly and
nothing looked wrong, but ai-api runs on ai-internal and every trace it sent
would have failed silently in Phase 6. HOSTNAME=0.0.0.0 fixes it; both
networks now return 200.

External network name. ai-internal does not exist - Compose created it as
azureuser_ai-internal under the host's shared project name. Verified both ways
on the host: with name: a container joins and reaches pg-ai, without it up
fails with "network ai-internal declared as external, but could not be found".

Healthcheck. Was probing localhost, which failed for the bind reason above and
marked the container unhealthy while it served fine. Restored to 127.0.0.1,
which now works because the bind is genuinely fixed rather than worked around.

Signup policy. AUTH_DISABLE_SIGNUP false. Langfuse keeps its own user table
behind Authelia; closed signup with no seeded user means nobody can log in at
all. Accepted for the PoC because Authelia is the real gate - revisit before
handover.

Phase 2 gate passed on the host with these applied: 302 to the auth portal,
certificate issued, AD + Duo login confirmed, and a test trace verified in the
database rather than trusted from the API response.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 10:38:04 +10:00

84 lines
3.3 KiB
YAML

# =============================================================================
# langfuse-compose.yml -> deployed to ~/langfuse-compose.yml on lin001
#
# Deployed early, deliberately (spec Phase 2): from here on, every experiment is
# traced. MIT licensed, self-hosted, no licence cost.
#
# ai-internal is created by ai-compose.yml - bring that up first.
#
# docker compose -f ~/langfuse-compose.yml up -d
# =============================================================================
services:
lf-db:
image: postgres:16-alpine
container_name: lf-db
restart: unless-stopped
networks: [ai-internal]
env_file:
- /home/azureuser/ai/langfuse.env # 0600, not in Git
environment:
POSTGRES_DB: langfuse
POSTGRES_USER: langfuse
PGDATA: /var/lib/postgresql/data/pgdata
# POSTGRES_PASSWORD comes from langfuse.env.
volumes:
- /datadisk/langfuse/db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U langfuse -d langfuse"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }
langfuse:
image: langfuse/langfuse:2
container_name: langfuse
restart: unless-stopped
depends_on:
lf-db:
condition: service_healthy
networks: [ai-internal, proxy]
env_file:
- /home/azureuser/ai/langfuse.env # 0600, not in Git
environment:
NEXTAUTH_URL: https://lf.yokogawa.tech
TELEMETRY_ENABLED: "false"
# Langfuse is a Next.js standalone server: it binds to whatever $HOSTNAME
# resolves to, NOT to all interfaces. This container is on two networks, and
# the container hostname resolves to its PROXY address - so without this it
# listens on proxy only and is unreachable from ai-internal, where ai-api
# will run. Verified on the host: proxy HTTP 200, ai-internal HTTP 000.
# Binding 0.0.0.0 makes it listen on both, and on loopback.
HOSTNAME: "0.0.0.0"
# Langfuse keeps its own user table behind Authelia. Signup is left OPEN so
# anyone who has already cleared AD + Duo can create their own account -
# with it closed and no user seeded, nobody can log in at all. Accepted
# for the PoC because Authelia is the real gate. Revisit before handover.
AUTH_DISABLE_SIGNUP: "false"
# DATABASE_URL, NEXTAUTH_SECRET and SALT come from langfuse.env.
healthcheck:
# Works because HOSTNAME=0.0.0.0 above makes it bind all interfaces including
# loopback. Without that env var this probe fails while the app serves fine.
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/public/health || exit 1"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }
networks:
# Created by ai-compose.yml, which runs under the shared 'azureuser' project,
# so Compose named it azureuser_ai-internal. Without the name: line the lookup
# is for a network literally called 'ai-internal' and up fails with
# "network ai-internal declared as external, but could not be found".
ai-internal:
external: true
name: azureuser_ai-internal
# proxy was created by hand, not by Compose, so it carries no project prefix.
proxy:
external: true