services: app: build: . image: php-todo-list ports: - "8080:80" environment: APP_DEBUG: "${APP_DEBUG:-false}" # Generated files (SQLite DB + JWT signing key) go here, on their own # volume so they don't overlap the bind mount below. STORAGE_PATH: /var/www/storage # Leave blank to auto-generate a secret into the storage volume on first run. JWT_SECRET: "${JWT_SECRET:-}" JWT_TTL: "${JWT_TTL:-86400}" volumes: # Live source: edit on the host, no image rebuild needed. - .:/var/www/html # Persistent storage, kept outside /var/www/html to avoid a nested mount. - storage:/var/www/storage restart: unless-stopped # Optional Vite dev server. Start it with: docker compose --profile frontend up -d # Without the profile, `docker compose up -d` runs the API alone. web: build: ./web image: php-todo-web profiles: ["frontend"] ports: - "5173:5173" environment: # /api is proxied to the API container on the compose network. VITE_PROXY_TARGET: "http://app:80" volumes: - ./web:/app # Keep the image's platform-specific node_modules; don't let the host's shadow them. - web_node_modules:/app/node_modules depends_on: - app restart: unless-stopped volumes: storage: web_node_modules: