Files
codexpi/docker-compose.yml

178 lines
5.5 KiB
YAML
Raw Normal View History

2026-11-14 20:23:59 +01:00
name: codexpi
x-app: &app
image: ${CODEXPI_IMAGE:-codexpi-app:local}
build:
context: .
target: production
args:
CODEX_VERSION: ${CODEX_VERSION:-latest}
CLAUDE_CODE_VERSION: ${CLAUDE_CODE_VERSION:-latest}
OPENCODE_VERSION: ${OPENCODE_VERSION:-latest}
env_file:
- .env
restart: unless-stopped
init: true
user: "${PUID:-1000}:${PGID:-1000}"
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:size=128m,mode=1777
volumes:
- ./data:/app/data
depends_on:
data-permissions:
condition: service_completed_successfully
services:
data-permissions:
image: codexpi-app:local
user: "0:0"
restart: "no"
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
volumes:
- ./data:/app/data
entrypoint: ["sh", "-c"]
command: ["chown -R \"$${PUID}:$${PGID}\" /app/data && chmod 700 /app/data"]
environment:
PUID: "${PUID:-1000}"
PGID: "${PGID:-1000}"
django-web:
<<: *app
command: >-
sh -c "python3 manage.py migrate --noinput &&
python3 -m uvicorn django_ui.asgi:application --host 0.0.0.0 --port 8001 --proxy-headers --forwarded-allow-ips='*'"
environment:
CODEXPI_DATA_DIR: /app/data
CODEXPI_COOKIE_SECURE: "${CODEXPI_COOKIE_SECURE:-false}"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8001/api/bootstrap', timeout=3)"]
interval: 15s
timeout: 5s
retries: 5
start_period: 20s
api:
<<: *app
# Codex workspace-write uses Bubblewrap, which creates an unprivileged user
# namespace. Docker's default seccomp profile blocks that syscall before
# Bubblewrap can apply its own (stricter) filesystem sandbox.
security_opt:
- no-new-privileges:true
- seccomp:unconfined
command: >-
python3 -m uvicorn codexpi.main:app --app-dir /app/backend
--host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips='*'
environment:
CODEXPI_BASE_DIR: /app
CODEXPI_DATA_DIR: /app/data
CODEXPI_DATABASE: /app/data/codexpi.db
CODEXPI_HOST: 0.0.0.0
CODEXPI_PORT: 8000
CODEXPI_AUTH_BACKEND: django
CODEXPI_CONTAINERIZED: "true"
CODEXPI_COOKIE_SECURE: "${CODEXPI_COOKIE_SECURE:-false}"
CODEXPI_BROWSER_WEBDRIVER_URL: http://browser:4444
CODEXPI_BROWSER_CONTAINER: codexpi-browser-1
CODEXPI_BROWSER_SCREENSHOTS: /app/data/browser/screenshots
CODEXPI_APP_STORE_AUTH_URL: "${CODEXPI_APP_STORE_AUTH_URL:-http://192.168.1.63:16002/auth/me}"
HOME: /home/codex
group_add:
- "${DOCKER_GID:-998}"
volumes:
- ./data:/app/data
- type: bind
source: /home/config
target: /home/config
bind:
propagation: rslave
- ${CODEXPI_HOME:-/home/config}/.codex:/home/codex/.codex
- ${CODEXPI_HOME:-/home/config}/.claude:/home/codex/.claude
- ${CODEXPI_HOME:-/home/config}/.config/opencode:/home/codex/.config/opencode
- ${CODEXPI_HOME:-/home/config}/.local/share/opencode:/home/codex/.local/share/opencode
- ${CODEXPI_HOME:-/home/config}/.config/gh:/home/codex/.config/gh
- /var/run/docker.sock:/var/run/docker.sock
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=3)"]
interval: 15s
timeout: 5s
retries: 5
start_period: 15s
browser:
image: ${CODEXPI_BROWSER_IMAGE:-selenium/standalone-chromium:latest}
command: ["/opt/bin/codexpi-browser-entrypoint"]
restart: unless-stopped
shm_size: "1gb"
environment:
SE_NODE_MAX_SESSIONS: "1"
SE_VNC_NO_PASSWORD: "true"
SE_SCREEN_WIDTH: "1440"
SE_SCREEN_HEIGHT: "900"
SE_SCREEN_DEPTH: "24"
volumes:
- browser-home:/home/seluser
- ./scripts/browser-entrypoint.sh:/opt/bin/codexpi-browser-entrypoint:ro
security_opt:
- no-new-privileges:true
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
user: "101:101"
command:
- /bin/sh
- -c
- |
django_upstream="$${CODEXPI_DJANGO_UPSTREAM:-django-web}"
api_upstream="$${CODEXPI_API_UPSTREAM:-api}"
echo "Attente de Django ($${django_upstream}) et FastAPI ($${api_upstream})…"
until wget -qO- "http://$${django_upstream}:8001/api/bootstrap" >/dev/null 2>&1 \
&& wget -qO- "http://$${api_upstream}:8000/api/health" >/dev/null 2>&1; do
sleep 2
done
echo "upstream django_ui { server $${django_upstream}:8001; }" > /tmp/codexpi-upstreams.conf
echo "upstream fastapi_api { server $${api_upstream}:8000; }" >> /tmp/codexpi-upstreams.conf
cat /tmp/codexpi-upstreams.conf
exec nginx -g 'daemon off;'
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
tmpfs:
- /tmp:size=16m,mode=1777
- /var/cache/nginx:size=16m,mode=0755
volumes:
- ./deploy/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "${CODEXPI_BIND_ADDRESS:-0.0.0.0}:${CODEXPI_PROXY_PORT:-8765}:8080"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/proxy-health"]
interval: 15s
timeout: 5s
retries: 5
start_period: 30s
depends_on:
django-web:
condition: service_healthy
api:
condition: service_healthy
browser:
condition: service_started
volumes:
browser-home: