Skip to content
h8d13 edited this page Oct 31, 2026 · 15 revisions

Setting up dev

The host needs a container engine only (docker compose or podman-compose): no node, no pnpm. compose.dev.yaml bind-mounts the checkout and runs Vite (client) + tsx watch (server); node_modules live in volumes.

docker compose -f compose.dev.yaml up --build

Building needs BuildKit (Dockerfile.dev and Dockerfile.prod use COPY --chmod and cache mounts). Docker only uses it with the buildx plugin installed (docker-buildx on Arch, docker-buildx-plugin on Debian/Ubuntu); without it the build falls back to the classic builder and fails with the --chmod option requires BuildKit. Podman builds them natively.

http://localhost:5173, then useToken("dev") in the DevTools console. Edits on the host hot-reload: Vite HMR for the client, tsx watch restarts the server. Ports are published on host loopback only (5173, 4991, 40000 tcp+udp).

Every start runs pnpm install --frozen-lockfile into the volumes, so a pulled lockfile change is picked up by restarting the container. Everything else runs inside it as well:

docker compose -f compose.dev.yaml exec caesar pnpm magic   # format + types + lint
docker compose -f compose.dev.yaml exec caesar pnpm test
docker compose -f compose.dev.yaml exec caesar pnpm add -F @caesar/server <pkg>

Dev data lives in apps/server/data/ (gitignored); delete it to start over. docker compose -f compose.dev.yaml down -v also drops the node_modules volumes (pnpm's store lives inside the root one) and the cached pnpm binary.

Rootful docker writes container-created files (apps/server/data, build output) as root into the checkout; rootless podman/docker map them to your user.

Migration from bun to pnpm was done for two reasons:

  • Allows better handling of deps/conf files.
  • Package manager bun was segfaulting/sigill during dev.

Dependencies

pnpm install fetches mediasoup's prebuilt worker from its releases (linux x64/arm64, kernel 6/7, macOS arm64, windows x64), so nothing gets compiled.

Dependencies used by more than one package are pinned once in the catalog: of pnpm-workspace.yaml and referenced as "catalog:" in each package.json, so overlapping packages resolve to a single copy. catalogMode: strict makes pnpm add of a cataloged dependency use the catalog; bump versions there.


Prod-dev testing

compose.prod-dev.yaml builds the checkout with Dockerfile.prod (BuildKit, see Setting up dev) and serves it on https://localhost:8443 (own project, ports and container names, so it runs next to a real prod stack):

docker compose -f compose.prod-dev.yaml up --build
docker compose -f compose.prod-dev.yaml down    # wipes app data

App data lives in the container on purpose, so every run after down starts clean (new owner token in docker logs caesar-prod-dev). The UI shows version dev; prefix CAESAR_BUILD_VERSION=$(git rev-parse --short HEAD) to stamp the commit. WebRTC uses 41000 (tcp+udp).

Self-signed cert from Caddy's local CA, so https://localhost:8443 works without DNS or a public CA. Can also go to DevTools look out for alt-svc to say h3 (HTTP3 working).

Trust the local CA once. Clicking through the warning loads pages, but service workers (sw.js, push) refuse untrusted certs: SecurityError: Failed to register a ServiceWorker ... SSL certificate error. The CA lives in its own volume, so it survives down and rebuilds:

docker cp caesar-caddy-prod-dev:/data/caddy/pki/authorities/local/root.crt caddy-prod-dev-root.crt
sudo trust anchor --store caddy-prod-dev-root.crt # p11-kit (Arch, Fedora)

Restart the browser after. Undo with sudo trust anchor --remove caddy-prod-dev-root.crt; removing the volume (down -v) creates a new CA that needs trusting again.

With podman: podman-compose / podman cp instead, everything else is the same.

Discord Alternative per your rules. Fork of Sharkord.

Clone this wiki locally