Skip to content

Local Development Setup

Two modes are available. The Docker Stack is recommended for day-to-day development.

ModeCommandBest for
Docker Stack (recommended)npm run dev:stack:upDay-to-day development
Infra-Only + Host Appsnpm run dev:infra:up + npm run devNative 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/.env

apps/webapp/.env and apps/marketing/.env are pre-filled — no changes needed.

For apps/server/.env, fill in the following values:

VariableHow to get it
APP_KEYcd apps/server && node ace generate:key
BETTER_AUTH_SECRETcd apps/server && node ace generate:key
HEALTH_SECRETcd apps/server && node ace generate:key
REDIS_PASSWORDFrom 1Password vault
RESEND_API_KEYFrom 1Password vault
BREVO_API_KEYFrom 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.sh

What the script does:

  1. Installs mkcert (if not present)
  2. Installs a local CA into your system trust store (may prompt for your password)
  3. Generates infra/local_dev/certs/local.cert.pem and certs/local.key.pem, covering jubiloop.localhost and *.jubiloop.localhost
  4. Linux only: adds /etc/hosts entries for all five domains (macOS resolves *.localhost automatically — no /etc/hosts changes needed)

Only run once per machine

Re-run only if you see certificate errors in the browser. It is safe to run again.


Starting up

bash
# Start in background (recommended)
npm run dev:stack:up

# Start in foreground (see all logs in terminal)
npm run dev:stack

First 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

ServiceWhat it does
installRuns npm ci if lockfile changed, then builds shared-types and api-client. Exits when done.
postgresPostgreSQL 15 on port 5433
redisRedis 7 on port 6379
serverRuns node ace migration:run --force then npm run dev with HMR
webappVite dev server with HMR
marketingNext.js dev server with Fast Refresh
ui-prototypeVite dev server for the design prototype (React 18 mock-data sandbox)
packagestsup --watch for api-client and shared-types
docsVitePress dev server
caddyHTTPS reverse proxy

Migrations run automatically on every stack start (via node ace migration:run --force in the server container's startup command).

Local URLs

AppURL
Web App (React)https://app.jubiloop.localhost
API (AdonisJS)https://api.jubiloop.localhost
Marketing (Next.js)https://jubiloop.localhost
UI Prototypehttps://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-cli

Container names (Docker Stack)

ServiceContainer name
postgresjubiloop_dev_postgres
redisjubiloop_dev_redis
installjubiloop_dev_install
serverjubiloop_dev_server
webappjubiloop_dev_webapp
marketingjubiloop_dev_marketing
ui-prototypejubiloop_dev_ui_prototype
packagesjubiloop_dev_packages
docsjubiloop_dev_docs
caddyjubiloop_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 npm

The 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

SectionFeatures
ServicesStart/stop full stack or infra-only, restart individual services, tail logs, shell into any container, open URLs in browser
Server & DatabaseRun/rollback migrations, seed database, open backend REPL, run setup:dev
ConnectionsOpen psql shell, Redis CLI, copy DB connection URL to clipboard
Code QualityLint, lint-fix, format, run tests, build, type-check, security audit
MaintenanceRebuild 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

ServiceHost portDocker Stack containerInfra-only container
PostgreSQL5433jubiloop_dev_postgresjubiloop_local_dev_postgres
Redis6379jubiloop_dev_redisjubiloop_local_dev_redis
Caddy80, 443jubiloop_dev_caddyjubiloop_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:fresh

Never 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    # HTTPS

Next: Git Worktrees for parallel development across branches

Built with ❤️ by the Jubiloop team