Skip to content

feat: add Docker Compose for single-command local development - #28

Open
AtherBilal wants to merge 1 commit into
10xapp:mainfrom
AtherBilal:feat/docker-compose
Open

feat: add Docker Compose for single-command local development#28
AtherBilal wants to merge 1 commit into
10xapp:mainfrom
AtherBilal:feat/docker-compose

Conversation

@AtherBilal

@AtherBilal AtherBilal commented Apr 1, 2026

Copy link
Copy Markdown

Summary

Getting Core running locally currently requires installing Node.js, Python, uv, and managing two separate processes. This PR adds Docker Compose so the entire stack comes up with a single command and a single env file — no language runtimes needed on the host.

cp .env.example .env    # one file for both services
# fill in Supabase credentials, then:
docker compose up

Changes

  • .env.example — single env file for both API and frontend (replaces the need to configure two separate files)
  • docker-compose.yml — two-service dev setup with health checks, volume mounts, and configurable ports
  • core-api/Dockerfile — Python 3.13 slim image running uvicorn with hot reload
  • core-web/Dockerfile — Node 22 slim image running Vite dev server with hot reload
  • core-api/.dockerignore / core-web/.dockerignore — keeps images lean
  • README.md — Docker Compose section added to Quick Start

Details

  • Single .env — one file at the repo root configures both services. The per-service env files (core-api/.env, core-web/.env) still work for non-Docker setups
  • Hot reload — source directories are volume-mounted, so edits on the host are reflected immediately without rebuilding
  • Health checks — the web container waits for the API to report healthy before starting
  • Port customizationCORE_API_PORT=9000 CORE_WEB_PORT=4000 docker compose up
  • Development only — this runs dev servers (uvicorn with reload, Vite dev), not a production build
  • No image proxycore-image-proxy is a Cloudflare Worker and not needed for local dev (the API falls back to presigned R2 URLs)

Note: Database migrations still require the Supabase CLI on the host (supabase db push). Docker handles only the API and frontend services.

Test plan

  • docker compose up starts both services, API healthy before web starts
  • Frontend loads at http://localhost:3000
  • API responds at http://localhost:8000
  • Edit a Python file → API auto-reloads
  • Edit a React component → Vite hot-reloads in browser
  • Custom ports work: CORE_API_PORT=9000 docker compose up
  • docker compose down cleans up cleanly

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Added a Docker Compose quick-start describing how to run API and frontend together locally, expected URLs, hot-reload behavior, and port override examples.
  • Chores

    • Added a containerized dev setup for API and frontend with health checks, Docker build/context exclusions, and a root .env template documenting required runtime/Supabase variables plus optional integrations and AI key placeholders.

@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Docker Compose-based local development for the core stack: root .env.example, docker-compose.yml, Dockerfiles and .dockerignore for core-api and core-web, plus README instructions to run both services with healthchecks, hot-reload mounts, and documented environment variables.

Changes

Cohort / File(s) Summary
Orchestration & env template
docker-compose.yml, .env.example
Adds a multi-service Docker Compose setup for api and web with env-driven host ports, service healthcheck and start ordering, mounts for hot reload, and a comprehensive .env.example documenting required Supabase/JWT/API vars and optional Sentry, PostHog, R2, OAuth, and AI keys.
Core API containerization
core-api/Dockerfile, core-api/.dockerignore
Introduces a Python 3.13-slim Dockerfile that installs requirements, copies app code, exposes port 8000, and runs python dev.py (development server); .dockerignore excludes virtualenvs, caches, .env, and .git.
Core Web containerization
core-web/Dockerfile, core-web/.dockerignore
Adds a Node 22-slim Dockerfile for the Vite app using npm ci, copies minimal runtime files, exposes port 3000, and runs the dev server (npm run dev -- --host); .dockerignore excludes node_modules, dist, .env, and .git.
Documentation
README.md
Adds "Alternative: Docker Compose" subsection instructing to copy .env.example to .env, run Supabase CLI link/db push from core-api, start the stack with docker compose up, notes expected local URLs, hot-reload behavior, and port override examples.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(240,248,255,0.5)
    participant Dev as "Developer"
    end
    rect rgba(224,255,255,0.5)
    participant DC as "Docker Compose"
    participant API as "api (Python)"
    participant WEB as "web (Vite)"
    participant SB as "Supabase"
    end

    Dev->>DC: docker compose up
    DC->>API: build & start container
    API->>API: serve healthcheck endpoint
    DC->>WEB: wait for API healthy → start web
    Dev->>WEB: open frontend (http://localhost:3000)
    WEB->>API: HTTP API requests
    API->>SB: DB / Auth requests
    SB-->>API: responses
    API-->>WEB: API responses
    WEB-->>Dev: render UI
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I hopped in with compose and keys,
Files aligned like tidy peas,
API hums, the frontend springs,
Volumes mount and dev mode sings,
I nibble logs and leave fresh greens. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and clearly describes the main change: adding Docker Compose to enable single-command local development, which aligns perfectly with the primary objective and changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
docker-compose.yml (1)

11-16: Health check implementation is functional but consider a lighter alternative.

The current healthcheck uses Python to import httpx and make an HTTP request. While functional (httpx is confirmed in requirements.txt), this approach:

  • Spawns a Python interpreter every 10 seconds
  • Adds overhead compared to native tools
⚡ Optional: Use curl for lighter health checks

If you want to reduce overhead, install curl in the Dockerfile and use it for health checks:

In core-api/Dockerfile:

 FROM python:3.13-slim
 
+RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
+
 WORKDIR /app

In docker-compose.yml:

     healthcheck:
-      test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/')"]
+      test: ["CMD", "curl", "-f", "http://localhost:8000/"]
       interval: 10s

However, the current httpx-based approach works fine for development and avoids adding dependencies to the image.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 11 - 16, Replace the Python/httpx
healthcheck to a lighter curl-based check: update the docker-compose healthcheck
"test" to call curl with a fail-on-error flag (e.g., curl -f
http://localhost:8000/) and ensure the image contains curl by adding its
installation to the Dockerfile used to build the service (install curl in the
Dockerfile build steps for the core API image). Locate the healthcheck block in
docker-compose.yml (the test key using python -c import httpx...) and the
service Dockerfile (add package install for curl), then update the test to use
curl so the container runs a native binary instead of spawning Python every
interval.
core-web/Dockerfile (1)

1-14: LGTM! Consider adding a non-root user for defense in depth.

The Dockerfile uses npm ci for reproducible installs and correctly layers package files before application code for build caching.

Similar to the API Dockerfile, running as root is acceptable for development but adding a non-root user would be a good practice:

🔒 Optional: Add non-root user
 FROM node:22-slim
 
 WORKDIR /app
 
+# Create non-root user
+RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
+USER appuser
+
 # Install dependencies
 COPY package.json package-lock.json ./
 RUN npm ci

Note: Ensure volume mounts use matching UID/GID to avoid permission issues.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-web/Dockerfile` around lines 1 - 14, Add a non-root user in the
Dockerfile to run the app for defense-in-depth: create a dedicated user and
group (e.g., app user), chown the WORKDIR (/app) and node_modules after running
npm ci, and switch to that user before CMD (ensure EXPOSE and CMD remain
unchanged). Use a fixed UID/GID or make them configurable via build args to help
match host volume mounts and avoid permission issues when mounting volumes.
Ensure commands that need root (install steps) run before switching to the
non-root user.
core-api/Dockerfile (1)

1-14: LGTM! Consider adding a non-root user for defense in depth.

The Dockerfile correctly installs dependencies before copying code, enabling layer caching. The CMD runs dev.py, which starts uvicorn with hot reload (confirmed by context snippet showing reload=True in dev.py).

For development containers, running as root is common but not ideal. Consider adding a non-root user as a good practice:

🔒 Optional: Add non-root user
 FROM python:3.13-slim
 
 WORKDIR /app
 
+# Create non-root user
+RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
+USER appuser
+
 # Install dependencies
 COPY requirements.txt .
 RUN pip install --no-cache-dir -r requirements.txt

Note: If added, ensure volume mounts use matching UID/GID on the host to avoid permission issues.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/Dockerfile` around lines 1 - 14, Add a non-root user to the
Dockerfile: create a group/user after using the python:3.13-slim base image,
chown the WORKDIR (/app) and any installed files to that user, and switch to
that user before the CMD ["python", "dev.py"] is executed; ensure the user
creation and chown steps occur after COPY and pip install to keep layer caching
benefits and note that host volume mounts may need matching UID/GID to avoid
permission issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@core-api/Dockerfile`:
- Around line 1-14: Add a non-root user to the Dockerfile: create a group/user
after using the python:3.13-slim base image, chown the WORKDIR (/app) and any
installed files to that user, and switch to that user before the CMD ["python",
"dev.py"] is executed; ensure the user creation and chown steps occur after COPY
and pip install to keep layer caching benefits and note that host volume mounts
may need matching UID/GID to avoid permission issues.

In `@core-web/Dockerfile`:
- Around line 1-14: Add a non-root user in the Dockerfile to run the app for
defense-in-depth: create a dedicated user and group (e.g., app user), chown the
WORKDIR (/app) and node_modules after running npm ci, and switch to that user
before CMD (ensure EXPOSE and CMD remain unchanged). Use a fixed UID/GID or make
them configurable via build args to help match host volume mounts and avoid
permission issues when mounting volumes. Ensure commands that need root (install
steps) run before switching to the non-root user.

In `@docker-compose.yml`:
- Around line 11-16: Replace the Python/httpx healthcheck to a lighter
curl-based check: update the docker-compose healthcheck "test" to call curl with
a fail-on-error flag (e.g., curl -f http://localhost:8000/) and ensure the image
contains curl by adding its installation to the Dockerfile used to build the
service (install curl in the Dockerfile build steps for the core API image).
Locate the healthcheck block in docker-compose.yml (the test key using python -c
import httpx...) and the service Dockerfile (add package install for curl), then
update the test to use curl so the container runs a native binary instead of
spawning Python every interval.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b90f745e-c4e1-4464-9942-3d30b2ade14e

📥 Commits

Reviewing files that changed from the base of the PR and between f50fda1 and 6e84e7b.

📒 Files selected for processing (7)
  • .env.example
  • README.md
  • core-api/.dockerignore
  • core-api/Dockerfile
  • core-web/.dockerignore
  • core-web/Dockerfile
  • docker-compose.yml

@AtherBilal
AtherBilal force-pushed the feat/docker-compose branch from 6e84e7b to 56b763e Compare April 1, 2026 17:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Around line 29-30: Update the .env.example to document that VITE_API_URL needs
to match any custom CORE_API_PORT; specifically, add an inline comment next to
VITE_API_URL explaining that if CORE_API_PORT is changed the host:port in
VITE_API_URL must be updated (show a short example like
VITE_API_URL=http://localhost:<CORE_API_PORT>) so users know to keep
VITE_API_URL and CORE_API_PORT in sync.
- Around line 26-30: Add a new environment variable entry and brief note to the
.env.example explaining ALLOWED_ORIGINS_ENV so users running the frontend on
non-default ports will add the matching origin for CORS; reference the existing
FRONTEND_URL variable and show that ALLOWED_ORIGINS_ENV should contain a
comma-separated list of allowed origins (e.g., http://localhost:3000) to be used
by the API CORS configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 05cb1c73-afe9-45f4-a9d1-8cfa573f0ef0

📥 Commits

Reviewing files that changed from the base of the PR and between 6e84e7b and 56b763e.

📒 Files selected for processing (7)
  • .env.example
  • README.md
  • core-api/.dockerignore
  • core-api/Dockerfile
  • core-web/.dockerignore
  • core-web/Dockerfile
  • docker-compose.yml
✅ Files skipped from review due to trivial changes (6)
  • core-api/.dockerignore
  • core-web/.dockerignore
  • core-api/Dockerfile
  • core-web/Dockerfile
  • README.md
  • docker-compose.yml

Comment thread .env.example
Comment thread .env.example
@AtherBilal
AtherBilal force-pushed the feat/docker-compose branch 2 times, most recently from b8ba9e4 to fa056ab Compare April 1, 2026 18:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
core-api/Dockerfile (2)

5-7: Consider pinning pip version for reproducible builds.

While using --no-cache-dir is appropriate, consider upgrading and pinning pip to avoid version-dependent behavior across builds.

📦 Proposed improvement
 # Install dependencies
+RUN pip install --upgrade pip==24.3.1
 COPY requirements.txt .
 RUN pip install --no-cache-dir -r requirements.txt
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/Dockerfile` around lines 5 - 7, Pin and upgrade pip before
installing requirements to ensure reproducible builds: add a step that upgrades
pip to a specific version (e.g., pip==23.2.1) prior to running the existing RUN
pip install --no-cache-dir -r requirements.txt command so the build always uses
the pinned pip; reference the existing COPY requirements.txt and the pip install
--no-cache-dir -r requirements.txt line when making this change.

1-3: Consider adding a non-root user for better security posture.

Even for development containers, running as root violates the principle of least privilege. Files written by the hot-reload server will be owned by root, which can cause permission issues on the host.

🔒 Proposed fix to add non-root user
 FROM python:3.13-slim
 
+RUN groupadd -r appuser && useradd -r -g appuser appuser
+
 WORKDIR /app
+
+RUN chown appuser:appuser /app

Then before the CMD instruction:

 EXPOSE 8000
 
+USER appuser
+
 # Development server with hot reload
 CMD ["python", "dev.py"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/Dockerfile` around lines 1 - 3, Add a non-root user in the
Dockerfile: create a dedicated user and group, chown the WORKDIR (/app) to that
user, and switch to it before CMD so the container does not run as root; update
the Dockerfile around the existing FROM python:3.13-slim and WORKDIR /app
entries to add the user/group creation, chown of /app, and a USER instruction so
the hot-reload server and any files it writes are owned by the non-root user.
core-web/Dockerfile (1)

1-3: Consider adding a non-root user for better security posture.

Similar to the API container, running as root violates the principle of least privilege. Files created by Vite's dev server and hot-reload will be owned by root.

🔒 Proposed fix to add non-root user
 FROM node:22-slim
 
+RUN groupadd -r appuser && useradd -r -g appuser appuser
+
 WORKDIR /app
+
+RUN chown appuser:appuser /app

Then before the CMD instruction:

 EXPOSE 3000
 
+USER appuser
+
 # Development server with hot reload
 CMD ["npm", "run", "dev", "--", "--host"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-web/Dockerfile` around lines 1 - 3, The Dockerfile currently runs as
root; add a non-root user and switch to it before CMD to follow least privilege:
create a group/user (e.g., app or node), set ownership of WORKDIR (/app) to that
user (chown), set appropriate permissions, and add a USER instruction so the
container runs as the new non-root user; ensure these changes occur after
installing deps and before the final CMD/ENTRYPOINT so Vite-created files are
owned by the non-root user.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@core-api/Dockerfile`:
- Around line 5-7: Pin and upgrade pip before installing requirements to ensure
reproducible builds: add a step that upgrades pip to a specific version (e.g.,
pip==23.2.1) prior to running the existing RUN pip install --no-cache-dir -r
requirements.txt command so the build always uses the pinned pip; reference the
existing COPY requirements.txt and the pip install --no-cache-dir -r
requirements.txt line when making this change.
- Around line 1-3: Add a non-root user in the Dockerfile: create a dedicated
user and group, chown the WORKDIR (/app) to that user, and switch to it before
CMD so the container does not run as root; update the Dockerfile around the
existing FROM python:3.13-slim and WORKDIR /app entries to add the user/group
creation, chown of /app, and a USER instruction so the hot-reload server and any
files it writes are owned by the non-root user.

In `@core-web/Dockerfile`:
- Around line 1-3: The Dockerfile currently runs as root; add a non-root user
and switch to it before CMD to follow least privilege: create a group/user
(e.g., app or node), set ownership of WORKDIR (/app) to that user (chown), set
appropriate permissions, and add a USER instruction so the container runs as the
new non-root user; ensure these changes occur after installing deps and before
the final CMD/ENTRYPOINT so Vite-created files are owned by the non-root user.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fb7c640-d790-4246-a0bb-5e1e7bdd5b20

📥 Commits

Reviewing files that changed from the base of the PR and between 56b763e and fa056ab.

📒 Files selected for processing (7)
  • .env.example
  • README.md
  • core-api/.dockerignore
  • core-api/Dockerfile
  • core-web/.dockerignore
  • core-web/Dockerfile
  • docker-compose.yml
✅ Files skipped from review due to trivial changes (4)
  • core-web/.dockerignore
  • core-api/.dockerignore
  • docker-compose.yml
  • README.md

@AtherBilal
AtherBilal force-pushed the feat/docker-compose branch from fa056ab to 5682361 Compare April 1, 2026 19:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
.env.example (1)

24-30: ⚠️ Potential issue | 🟠 Major

Document port overrides and URL sync in the env template.

Line 28 (FRONTEND_URL) and Line 29 (VITE_API_URL) currently assume default ports, but the PR advertises configurable ports. Add CORE_API_PORT/CORE_WEB_PORT and explicit sync notes so overrides don’t silently break frontend↔API communication.

Suggested update
 # ------------------------------------------------------------------------------
 # [REQUIRED] API
 # ------------------------------------------------------------------------------
 API_ENV=development
 DEBUG=false
+# Compose host port overrides
+CORE_API_PORT=8000
+CORE_WEB_PORT=3000
+
+# Keep this in sync with CORE_WEB_PORT when overriding
 FRONTEND_URL=http://localhost:3000
+# Keep this in sync with CORE_API_PORT when overriding
 VITE_API_URL=http://localhost:8000/api
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 24 - 30, Add explicit CORE_API_PORT and
CORE_WEB_PORT entries to the env template and update the FRONTEND_URL and
VITE_API_URL descriptions to warn that changing ports requires updating both
URLs (or using the CORE_*_PORT variables) so frontend↔API links remain in sync;
specifically add CORE_API_PORT and CORE_WEB_PORT variables, and modify the
FRONTEND_URL and VITE_API_URL lines (and their comments) to explain how to
construct/sync them when overriding ports so developers don't accidentally point
the frontend at the wrong port.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.env.example:
- Around line 24-30: Add explicit CORE_API_PORT and CORE_WEB_PORT entries to the
env template and update the FRONTEND_URL and VITE_API_URL descriptions to warn
that changing ports requires updating both URLs (or using the CORE_*_PORT
variables) so frontend↔API links remain in sync; specifically add CORE_API_PORT
and CORE_WEB_PORT variables, and modify the FRONTEND_URL and VITE_API_URL lines
(and their comments) to explain how to construct/sync them when overriding ports
so developers don't accidentally point the frontend at the wrong port.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96b70e1c-75ca-462d-b9cc-7e7628ced497

📥 Commits

Reviewing files that changed from the base of the PR and between fa056ab and 5682361.

📒 Files selected for processing (7)
  • .env.example
  • README.md
  • core-api/.dockerignore
  • core-api/Dockerfile
  • core-web/.dockerignore
  • core-web/Dockerfile
  • docker-compose.yml
✅ Files skipped from review due to trivial changes (5)
  • core-web/.dockerignore
  • core-api/.dockerignore
  • core-api/Dockerfile
  • core-web/Dockerfile
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docker-compose.yml

Self-hosters and contributors can now run both services with
`docker compose up` instead of installing Node, Python, and uv
separately. Includes Dockerfiles for core-api and core-web with
volume mounts for hot reload during development.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@AtherBilal
AtherBilal force-pushed the feat/docker-compose branch from 5682361 to 7b4d158 Compare April 1, 2026 19:51
@sonarqubecloud

sonarqubecloud Bot commented Apr 1, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
.env.example (4)

19-21: Consider using variable substitution to eliminate duplication.

Lines 20-21 require duplicating the Supabase URL and anon key (once for the backend, once for the frontend). This introduces maintenance burden and risk of mismatch. Docker Compose supports variable substitution:

♻️ Proposed refactor using variable substitution
 # Frontend Supabase config (must match the values above)
-VITE_SUPABASE_URL=https://your-project.supabase.co
-VITE_SUPABASE_ANON_KEY=your-anon-key
+VITE_SUPABASE_URL=${SUPABASE_URL}
+VITE_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}

This ensures the frontend always uses the backend's values and eliminates the "must match" constraint.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 19 - 21, The frontend .env entries
VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are duplicating backend values;
change them to use environment variable substitution (e.g., set
VITE_SUPABASE_URL=${SUPABASE_URL} and
VITE_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}) so Docker Compose / env file
resolution provides the backend values automatically; update .env.example to
demonstrate the substitution and ensure your compose/service config exposes
SUPABASE_URL and SUPABASE_ANON_KEY to the frontend.

1-8: Document the namespace pollution inherent in the shared .env approach.

The Docker Compose configuration loads the entire .env file into both the api and web containers without filtering. This means:

  • The web container receives backend-only secrets (e.g., SUPABASE_SERVICE_ROLE_KEY, R2_SECRET_ACCESS_KEY)
  • The API container receives frontend-only variables (e.g., VITE_* variables)

While not functionally broken for development (unused variables are ignored, and Vite only exposes VITE_-prefixed variables to the browser), this creates confusion about which service uses which variables and complicates security audits.

Consider documenting this limitation in the header comments, or refactor docker-compose.yml to explicitly declare each service's required variables using the environment: key instead of env_file: for clearer separation.

Alternative approach using explicit environment mapping

In docker-compose.yml:

services:
  api:
    environment:
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
      - SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY}
      # ... other API-only vars
  web:
    environment:
      - VITE_SUPABASE_URL=${VITE_SUPABASE_URL}
      - VITE_API_URL=${VITE_API_URL}
      # ... other VITE_ vars

This approach makes dependencies explicit and prevents namespace pollution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 1 - 8, Add a short note to the top of .env.example
explaining that using a shared .env with docker-compose env_file loads all
variables into every service (causing namespace pollution) and that backend-only
secrets (e.g., SUPABASE_SERVICE_ROLE_KEY, R2_SECRET_ACCESS_KEY) may end up in
the web container while frontend VITE_* variables are available to the API;
suggest either documenting this limitation or refactoring docker-compose.yml to
map each service's variables explicitly via the environment: key (listing
API-only vars like SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY
and web-only VITE_* vars) to avoid accidental exposure and make intent explicit.

30-31: VITE_API_URL is misplaced in the API section.

VITE_API_URL is a frontend environment variable (the VITE_ prefix means it's exposed to the browser by Vite). Placing it under [REQUIRED] API is confusing since it belongs to the web service. Consider moving it to a [REQUIRED] Frontend subsection or grouping it with the other VITE_ variables.

♻️ Proposed reorganization
 # ------------------------------------------------------------------------------
 # [REQUIRED] API
 # ------------------------------------------------------------------------------
 API_ENV=development
 DEBUG=false
 # If you override CORE_WEB_PORT, update this to match (e.g. http://localhost:4000)
 FRONTEND_URL=http://localhost:3000
+
+# ------------------------------------------------------------------------------
+# [REQUIRED] Frontend
+# ------------------------------------------------------------------------------
 # If you override CORE_API_PORT, update this to match (e.g. http://localhost:9000/api)
 VITE_API_URL=http://localhost:8000/api
+
+# ------------------------------------------------------------------------------
+# [REQUIRED] CORS
+# ------------------------------------------------------------------------------
 # Comma-separated CORS origins. Only needed if running on non-default ports.
 # ALLOWED_ORIGINS_ENV=http://localhost:4000
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 30 - 31, VITE_API_URL is a frontend-specific env
var (Vite-exposed) but is currently listed under the [REQUIRED] API section;
move the VITE_API_URL entry out of the API block and into a new or existing
[REQUIRED] Frontend subsection (or alongside other VITE_ variables), update its
explanatory comment to mention it should match CORE_API_PORT if overridden, and
ensure other VITE_* variables are grouped together so frontend-related settings
are not mixed with backend/API settings.

28-29: Add clarification about port differences between Docker and non-Docker setups.

The root .env.example declares FRONTEND_URL=http://localhost:3000 for Docker Compose, while core-api/.env.example declares FRONTEND_URL=http://localhost:5173 for native Vite dev server. This difference may confuse developers switching between setup modes. Add a note in the root .env.example to clarify:

Proposed clarification
 # If you override CORE_WEB_PORT, update this to match (e.g. http://localhost:4000)
+# Note: Non-Docker setups use port 5173 (native Vite), Docker uses 3000
 FRONTEND_URL=http://localhost:3000
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 28 - 29, Update the root .env.example comment next
to FRONTEND_URL to explicitly note the port difference between Docker Compose
and native Vite dev mode: explain that Docker Compose maps the frontend to
CORE_WEB_PORT (default http://localhost:3000) while developing natively Vite
uses port 5173 (see core-api/.env.example), and remind developers to update
FRONTEND_URL to match if they override CORE_WEB_PORT or run the Vite dev server
locally. Include references to FRONTEND_URL, CORE_WEB_PORT, and
core-api/.env.example so readers know where to look and what to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Around line 28-33: The comment points out that when overriding CORE_WEB_PORT
or CORE_API_PORT the example envs FRONTEND_URL and VITE_API_URL must be
updated—and you must also remind users to update ALLOWED_ORIGINS_ENV so the new
frontend origin is allowed via CORS; update the .env.example comments around
FRONTEND_URL, VITE_API_URL and ALLOWED_ORIGINS_ENV to explicitly state that
ALLOWED_ORIGINS_ENV should be updated to include the new FRONTEND_URL (or
matching origin) whenever CORE_WEB_PORT/CORE_API_PORT are changed and show a
short example of the comma-separated format using the updated port.

---

Nitpick comments:
In @.env.example:
- Around line 19-21: The frontend .env entries VITE_SUPABASE_URL and
VITE_SUPABASE_ANON_KEY are duplicating backend values; change them to use
environment variable substitution (e.g., set VITE_SUPABASE_URL=${SUPABASE_URL}
and VITE_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}) so Docker Compose / env file
resolution provides the backend values automatically; update .env.example to
demonstrate the substitution and ensure your compose/service config exposes
SUPABASE_URL and SUPABASE_ANON_KEY to the frontend.
- Around line 1-8: Add a short note to the top of .env.example explaining that
using a shared .env with docker-compose env_file loads all variables into every
service (causing namespace pollution) and that backend-only secrets (e.g.,
SUPABASE_SERVICE_ROLE_KEY, R2_SECRET_ACCESS_KEY) may end up in the web container
while frontend VITE_* variables are available to the API; suggest either
documenting this limitation or refactoring docker-compose.yml to map each
service's variables explicitly via the environment: key (listing API-only vars
like SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY and web-only
VITE_* vars) to avoid accidental exposure and make intent explicit.
- Around line 30-31: VITE_API_URL is a frontend-specific env var (Vite-exposed)
but is currently listed under the [REQUIRED] API section; move the VITE_API_URL
entry out of the API block and into a new or existing [REQUIRED] Frontend
subsection (or alongside other VITE_ variables), update its explanatory comment
to mention it should match CORE_API_PORT if overridden, and ensure other VITE_*
variables are grouped together so frontend-related settings are not mixed with
backend/API settings.
- Around line 28-29: Update the root .env.example comment next to FRONTEND_URL
to explicitly note the port difference between Docker Compose and native Vite
dev mode: explain that Docker Compose maps the frontend to CORE_WEB_PORT
(default http://localhost:3000) while developing natively Vite uses port 5173
(see core-api/.env.example), and remind developers to update FRONTEND_URL to
match if they override CORE_WEB_PORT or run the Vite dev server locally. Include
references to FRONTEND_URL, CORE_WEB_PORT, and core-api/.env.example so readers
know where to look and what to change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b0aa4ff-29aa-4a29-9e8d-f2413a09b628

📥 Commits

Reviewing files that changed from the base of the PR and between 5682361 and 7b4d158.

📒 Files selected for processing (7)
  • .env.example
  • README.md
  • core-api/.dockerignore
  • core-api/Dockerfile
  • core-web/.dockerignore
  • core-web/Dockerfile
  • docker-compose.yml
✅ Files skipped from review due to trivial changes (5)
  • core-web/.dockerignore
  • core-api/.dockerignore
  • README.md
  • core-api/Dockerfile
  • docker-compose.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • core-web/Dockerfile

Comment thread .env.example
Comment on lines +28 to +33
# If you override CORE_WEB_PORT, update this to match (e.g. http://localhost:4000)
FRONTEND_URL=http://localhost:3000
# If you override CORE_API_PORT, update this to match (e.g. http://localhost:9000/api)
VITE_API_URL=http://localhost:8000/api
# Comma-separated CORS origins. Only needed if running on non-default ports.
# ALLOWED_ORIGINS_ENV=http://localhost:4000

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Port override guidance is incomplete: ALLOWED_ORIGINS_ENV must also be synced.

The inline comments explain that FRONTEND_URL must match CORE_WEB_PORT and VITE_API_URL must match CORE_API_PORT, but there's no guidance that ALLOWED_ORIGINS_ENV must ALSO be updated when CORE_WEB_PORT changes (to allow the new frontend origin through CORS). This creates a broken configuration path.

📝 Proposed documentation update
 # If you override CORE_WEB_PORT, update this to match (e.g. http://localhost:4000)
 FRONTEND_URL=http://localhost:3000
 # If you override CORE_API_PORT, update this to match (e.g. http://localhost:9000/api)
 VITE_API_URL=http://localhost:8000/api
-# Comma-separated CORS origins. Only needed if running on non-default ports.
+# Comma-separated CORS origins. Must include FRONTEND_URL when overriding CORE_WEB_PORT.
+# Example: If CORE_WEB_PORT=4000, set ALLOWED_ORIGINS_ENV=http://localhost:4000
 # ALLOWED_ORIGINS_ENV=http://localhost:4000
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 28 - 33, The comment points out that when
overriding CORE_WEB_PORT or CORE_API_PORT the example envs FRONTEND_URL and
VITE_API_URL must be updated—and you must also remind users to update
ALLOWED_ORIGINS_ENV so the new frontend origin is allowed via CORS; update the
.env.example comments around FRONTEND_URL, VITE_API_URL and ALLOWED_ORIGINS_ENV
to explicitly state that ALLOWED_ORIGINS_ENV should be updated to include the
new FRONTEND_URL (or matching origin) whenever CORE_WEB_PORT/CORE_API_PORT are
changed and show a short example of the comma-separated format using the updated
port.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant