docker-compose.yml
Rama main · réplica de lectura del repositorio original.
services:
muselab:
build:
context: .
dockerfile: Dockerfile
args:
MUSE_UID: ${MUSELAB_UID:-1000}
MUSE_GID: ${MUSELAB_GID:-1000}
image: muselab:latest
container_name: muselab
restart: unless-stopped
ports:
# Bind to loopback by default — Docker's short syntax otherwise exposes
# the port on 0.0.0.0 (all interfaces), which on a public VPS leaves the
# service reachable from the internet with only the token as a barrier.
# To open it up (e.g. for phone access on the LAN), set MUSELAB_BIND=0.0.0.0
# in .env AND put a reverse proxy / firewall in front. See docs/quickstart.
- "${MUSELAB_BIND:-127.0.0.1}:${MUSELAB_PORT:-8765}:8765"
env_file:
- .env
environment:
# Force the Agent SDK to use OAuth (subscription), not console API key.
ANTHROPIC_API_KEY: ""
ANTHROPIC_AUTH_TOKEN: ""
# Inside the container the default workspace is always /data; the host
# path uses the legacy-compatible ARCHIVE_DIR variable below.
MUSELAB_ROOT: /data
# Never inherit a native-install host path from .env. Container session
# state is persisted by the /app/sessions bind mount below.
MUSELAB_SESSIONS_DIR: /app/sessions
# Keep mutable settings and third-party canonical transcripts in the
# same durable volume. File values override bootstrap env_file values.
MUSELAB_CONFIG_DIR: /app/sessions/config
MUSELAB_ENV_PATH: /app/sessions/config/.env
MUSELAB_ENV_OVERRIDE: "1"
XDG_STATE_HOME: /app/sessions/state
volumes:
# The default workspace exposed for browsing / editing.
# Set the legacy-compatible ARCHIVE_DIR in .env (defaults to ./data).
- ${ARCHIVE_DIR:-./data}:/data
# Reuse the host's claude CLI OAuth credentials.
# Default: ~/.claude. Override CLAUDE_HOME in .env for non-standard locations.
#
# SECURITY NOTE — this mount is read-WRITE on purpose, and that has a real
# blast radius: ~/.claude/.credentials.json holds your Pro/Max OAuth
# tokens, and the container (which runs an LLM agent that can execute
# tools) can read them. A read-only (`:ro`) mount is NOT a drop-in fix —
# the CLI needs write access to (a) refresh the OAuth token when it
# expires and (b) write session history under ~/.claude/projects/. Making
# the whole dir `:ro` breaks token refresh and session persistence.
#
# To reduce exposure on a shared/hostile host, mount ONLY the subpaths the
# CLI actually needs instead of the whole home dir, e.g.:
# - ${CLAUDE_HOME:-${HOME}/.claude}/.credentials.json:/home/muse/.claude/.credentials.json
# - ${CLAUDE_HOME:-${HOME}/.claude}/projects:/home/muse/.claude/projects
# That keeps the rest of ~/.claude (settings, other secrets) off the
# container. See SECURITY.md.
- ${CLAUDE_HOME:-${HOME}/.claude}:/home/muse/.claude
# Persist sessions, UI configuration and third-party transcripts.
- ./sessions:/app/sessions
# Resource ceilings mirror the systemd unit (see
# ~/.config/systemd/user/muselab.service): both deployment paths get the
# same "long-running but bounded" envelope. SDK subprocess + token
# buffering + cache usually sits well under 1 GiB; 4 GiB is the hard
# OOM-kill ceiling that protects the host from a memory leak.
mem_limit: 4g
mem_reservation: 1g
# Cap process / thread count to prevent fork-bomb-style runaway from a
# misbehaving MCP server or skill.
pids_limit: 4096
healthcheck:
# Use the dedicated /api/health endpoint — cheaper than rendering the
# full index.html on every probe (every 30s × 24h = 2880 probes/day).
# `curl` ships with the image and avoids the ~150ms Python startup cost.
test:
- CMD
- curl
- -fsS
- --max-time
- "3"
- http://127.0.0.1:8765/api/health
interval: 30s
timeout: 5s
retries: 3
start_period: 15s