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: