Appearance
Local Development Setup
Two modes are available. The Docker Stack is recommended for day-to-day development.
| Mode | Command | Best for |
|---|---|---|
| Docker Stack (recommended) | npm run dev:stack:up | Day-to-day development |
| Infra-Only + Host Apps | npm run dev:infra:up + npm run dev | Native debugging, macOS without VirtioFS |
One-Time Setup
1. Prerequisites
macOS: Docker Desktop required. After installing, enable VirtioFS for fast hot reload:
Docker Desktop → Settings → General → "Use Virtualization framework" → Apply & Restart
VirtioFS on macOS
Without VirtioFS, file-watching inside Docker containers is noticeably slow. Hot reload will work but feel laggy. Check this setting before proceeding.
Linux: Docker Engine + Docker Compose plugin. No extra settings required.
2. Copy environment files
bash
cp apps/server/.env.example apps/server/.env
cp apps/webapp/.env.example apps/webapp/.env
cp apps/marketing/.env.example apps/marketing/.envapps/webapp/.env and apps/marketing/.env are pre-filled — no changes needed.
For apps/server/.env, fill in the following values:
| Variable | How to get it |
|---|---|
APP_KEY | cd apps/server && node ace generate:key |
BETTER_AUTH_SECRET | cd apps/server && node ace generate:key |
HEALTH_SECRET | cd apps/server && node ace generate:key |
REDIS_PASSWORD | From 1Password vault |
RESEND_API_KEY | From 1Password vault |
BREVO_API_KEY | From 1Password vault |
Docker Stack overrides DB/Redis hosts
DB_HOST and REDIS_HOST are automatically overridden inside the stack. You only need to set the secrets above.
3. Set up HTTPS certificates
bash
cd infra/local_dev
chmod +x setup-https.sh
./setup-https.shWhat the script does:
- Installs
mkcert(if not present) - Installs a local CA into your system trust store (may prompt for your password)
- Generates
infra/local_dev/certs/local.cert.pemandcerts/local.key.pem, coveringjubiloop.localhostand*.jubiloop.localhost - Linux only: adds
/etc/hostsentries for all five domains (macOS resolves*.localhostautomatically — no/etc/hostschanges needed)
Only run once per machine
Re-run only if you see certificate errors in the browser. It is safe to run again.
Docker Stack (Recommended)
Starting up
bash
# Start in background (recommended)
npm run dev:stack:up
# Start in foreground (see all logs in terminal)
npm run dev:stackFirst run: ~2–3 minutes to download images, install dependencies, and build shared packages. Subsequent starts are near-instant unless package-lock.json changed.
What starts automatically
| Service | What it does |
|---|---|
install | Runs npm ci if lockfile changed, then builds shared-types and api-client. Exits when done. |
postgres | PostgreSQL 15 on port 5433 |
redis | Redis 7 on port 6379 |
server | Runs node ace migration:run --force then npm run dev with HMR |
webapp | Vite dev server with HMR |
marketing | Next.js dev server with Fast Refresh |
ui-prototype | Vite dev server for the design prototype (React 18 mock-data sandbox) |
packages | tsup --watch for api-client and shared-types |
docs | VitePress dev server |
caddy | HTTPS reverse proxy |
Migrations run automatically on every stack start (via node ace migration:run --force in the server container's startup command).
Local URLs
| App | URL |
|---|---|
| Web App (React) | https://app.jubiloop.localhost |
| API (AdonisJS) | https://api.jubiloop.localhost |
| Marketing (Next.js) | https://jubiloop.localhost |
| UI Prototype | https://lab.jubiloop.localhost |
| Docs (VitePress) | https://docs.jubiloop.localhost |
Daily commands
bash
npm run dev:stack:up # Start in background
npm run dev:stack:down # Stop all containers
npm run dev:stack:logs # Stream logs
npm run dev:stack:restart # Restart all containers
npm run dev:stack:rebuild # Rebuild images (after package.json changes)Running commands inside the stack
bash
# Run tests
docker exec -it jubiloop_dev_server node ace test
docker exec -it jubiloop_dev_server node ace test --suite unit
docker exec -it jubiloop_dev_server node ace test --suite functional
# Database
docker exec -it jubiloop_dev_server node ace migration:run
docker exec -it jubiloop_dev_server node ace repl
# Access databases directly
docker exec -it jubiloop_dev_postgres psql -U postgres -d jubiloop_dev_db
docker exec -it jubiloop_dev_redis redis-cliContainer names (Docker Stack)
| Service | Container name |
|---|---|
| postgres | jubiloop_dev_postgres |
| redis | jubiloop_dev_redis |
| install | jubiloop_dev_install |
| server | jubiloop_dev_server |
| webapp | jubiloop_dev_webapp |
| marketing | jubiloop_dev_marketing |
| ui-prototype | jubiloop_dev_ui_prototype |
| packages | jubiloop_dev_packages |
| docs | jubiloop_dev_docs |
| caddy | jubiloop_dev_caddy |
Dev TUI Dashboard
The Dev TUI is an optional interactive terminal dashboard for managing the local dev environment without memorising Docker commands.
bash
./dev-tui.sh # from repo root
npm run dev:tui # via npmThe script auto-builds a Go binary on first launch (no Go required — Docker is used as a fallback builder). The built binary lives at infra/local_dev/bin/dev-tui (gitignored) and is auto-rebuilt whenever source files change.
What the TUI provides
| Section | Features |
|---|---|
| Services | Start/stop full stack or infra-only, restart individual services, tail logs, shell into any container, open URLs in browser |
| Server & Database | Run/rollback migrations, seed database, open backend REPL, run setup:dev |
| Connections | Open psql shell, Redis CLI, copy DB connection URL to clipboard |
| Code Quality | Lint, lint-fix, format, run tests, build, type-check, security audit |
| Maintenance | Rebuild containers, install dependencies, clear Turbo cache, wipe volumes, run HTTPS cert setup |
Key features:
- Real-time health monitoring: Docker container status + HTTP health checks every 5 seconds
- Docker/Local execution mode toggle (
Tab) — runs commands inside containers or directly on host - Fuzzy command search (
/) across all 32 menu items - Scrollable output viewer for lint, test, and migration results
- Persistent preferences (
.tui-state.json, gitignored)
For full keyboard shortcuts, press ? inside the TUI.
Alternative: Infra-Only + Host Apps
See Host-Mode Development. Use this when:
- Native debugging with Node.js inspector
- macOS without VirtioFS enabled
- Running Japa tests directly on the host
Service Reference
| Service | Host port | Docker Stack container | Infra-only container |
|---|---|---|---|
| PostgreSQL | 5433 | jubiloop_dev_postgres | jubiloop_local_dev_postgres |
| Redis | 6379 | jubiloop_dev_redis | jubiloop_local_dev_redis |
| Caddy | 80, 443 | jubiloop_dev_caddy | jubiloop_local_dev_caddy_proxy |
Container names differ between modes
Docker Stack uses jubiloop_dev_*. Infra-only uses jubiloop_local_dev_*. Use the right name in docker exec commands.
Troubleshooting
HTTPS certificate errors: Re-run ./setup-https.sh, then restart Caddy (docker restart jubiloop_dev_caddy) and reopen your browser.
Hot reload is slow (macOS): Enable VirtioFS — see Step 1 of One-Time Setup above.
A container fails to start: Check logs: npm run dev:stack:logs. Try npm run dev:stack:rebuild if packages changed.
Migration fails: Check server logs: docker logs jubiloop_dev_server. For a local reset, use migration:fresh:
bash
docker exec -it jubiloop_dev_server node ace migration:freshNever use --batch=0
migration:rollback --batch=0 drops every table. Use migration:fresh for local resets only.
Port conflicts:
bash
lsof -i :5433 # PostgreSQL
lsof -i :6379 # Redis
lsof -i :80 # HTTP
lsof -i :443 # HTTPSNext: Git Worktrees for parallel development across branches