Skip to content

feat: add Docker dev environment with live reload#1

Open
WilsonNet wants to merge 1 commit intoprofusion:mainfrom
WilsonNet:feat/docker-dev-environment
Open

feat: add Docker dev environment with live reload#1
WilsonNet wants to merge 1 commit intoprofusion:mainfrom
WilsonNet:feat/docker-dev-environment

Conversation

@WilsonNet
Copy link
Copy Markdown

@WilsonNet WilsonNet commented Apr 6, 2026

Summary

Adds a `docker-compose.dev.yml` for local development with automatic live reload for both backend and frontend. No existing files are modified.

  • `docker-compose.dev.yml` — standalone dev stack using `manage.py runserver` (backend) and `pnpm dev` (frontend); same env file conventions as production
  • `dashboard/Dockerfile.dev` — lightweight Node image running Vite dev server with `src/` and `public/` mounted for HMR
  • `proxy/etc/nginx/templates/dev.conf.template` — dev nginx config that proxies `/` to Vite with WebSocket upgrade headers for HMR instead of serving static files
  • `docs/dev-environment.md` — contributor docs covering setup, live reload mechanics, migration workflow, edge cases, loading DB dumps, and the Docker Desktop inode caveat with workarounds

How to use

cp .env.example .env
cd dashboard && cp .env.example .env && cd ..
# Edit .env: set DB_PASSWORD and DJANGO_SECRET_KEY
docker compose -f docker-compose.dev.yml up -d --build

Access at `http://localhost:9000\` (same URL as production).

Live reload mechanisms

  • Backend — Django `manage.py runserver` with `StatReloader`; restarts automatically on any `.py` change. Chosen over gunicorn `--reload` which is unreliable with Docker volume mounts.
  • Frontend — Vite HMR over WebSocket forwarded through nginx. CSS-only changes inject without touching JS; TSX errors show browser overlay and auto-recover on fix.

TanStack Router codegen fix (`TSR_TMP_DIR`)

TanStack Router generates `routeTree.gen.ts` by writing a temp file then atomically renaming it to the destination. Atomic rename means: write the full content to a throwaway file first, then swap it into place in one instant — so nobody ever sees a half-written file.

The problem in Docker: the rename was crossing two different filesystem address spaces:

  • Temp file → `/dashboard/.tanstack/tmp/` (container's own internal storage)
  • Destination → `/dashboard/src/routeTree.gen.ts` (host bind mount)

Linux forbids rename across different address spaces (EXDEV error), so the route tree was never updated when new route files were added.

Fix: `TSR_TMP_DIR=/dashboard/src/.tanstack-tmp` moves the temp file inside the `src/` bind mount. Now both paths live in the same address space and the rename succeeds. The `src/.tanstack-tmp/` directory is gitignored.

This works on all setups (native Docker Engine on Linux, Docker Desktop on Linux/macOS, OrbStack) because the fix is about keeping both ends of the rename inside the same bind mount entry — not about which OS or VM layer is underneath.

Test plan

  • Stack builds and starts from scratch (`docker compose -f docker-compose.dev.yml up -d --build`)
  • `http://localhost:9000\`, `http://localhost:5173\`, `http://localhost:8000/api/schema/\` all return 200
  • Backend: add route + view → auto-reload → route serves correctly
  • Backend: `makemigrations` + `migrate` forward → column appears in DB
  • Backend: `migrate` backward → column removed from DB
  • Backend: `settings.py` change → auto-reload, server stays up
  • Backend: syntax error → container exits with clear traceback; recovers after fix + restart
  • Frontend: edit component → `hmr update` in logs, browser updates
  • Frontend: TSX syntax error → browser overlay, auto-recovers on fix
  • Frontend: add new route file → `routeTree.gen.ts` auto-updated by Vite plugin (no manual steps)
  • DB dump loading: `down -v` + `up -d` + `psql < dump.sql` sequence validated end-to-end

@WilsonNet WilsonNet force-pushed the feat/docker-dev-environment branch 6 times, most recently from 677eac3 to bfea109 Compare April 7, 2026 18:57
Adds docker-compose.dev.yml for local development with automatic live
reload for both backend and frontend. No changes to existing files.

- docker-compose.dev.yml: standalone dev stack (Django runserver +
  Vite HMR) using the same env file conventions as production
- dashboard/Dockerfile.dev: lightweight node image running pnpm dev
  with src/ and public/ mounted for HMR
- proxy/etc/nginx/templates/dev.conf.template: dev nginx config that
  proxies / to Vite (with WebSocket upgrade for HMR) instead of
  serving static files
- docs/dev-environment.md: contributor docs covering setup, live
  reload mechanics, migration workflow, edge cases (syntax errors,
  stale pyc, deleted files, circular imports, package.json changes),
  and the Docker Desktop inode caveat with workarounds
- extend inode caveat to cover macOS and add OrbStack as workaround
- remove redundant --build from initial setup command
- clarify when --build is needed and what it does
- add Swagger UI and ReDoc links to dev environment doc
- expose postgres port 5432 and document SQL client connection
@WilsonNet WilsonNet force-pushed the feat/docker-dev-environment branch from bfea109 to 5ade0c6 Compare April 7, 2026 19:00
@WilsonNet WilsonNet marked this pull request as ready for review April 7, 2026 19:04
Copy link
Copy Markdown
Collaborator

@giubrocchi giubrocchi left a comment

Choose a reason for hiding this comment

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

Nice, thanks for the help!

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.

2 participants