Docker
Building and running Crudy with the provided Dockerfiles.
Every scaffolded app ships a production Dockerfile and a dev Dockerfile.dev for both the backend and the frontend, plus a Makefile that wraps the most common compose commands for your app.
Dockerfiles per app
my-app/
backend/
Dockerfile # multi-stage: build, deps, runtime. Runs as non-root.
Dockerfile.dev # single stage. Runs `npx tsx watch server.ts`.
frontend/
Dockerfile # multi-stage: deps, builder, runner. Uses Next standalone output.
Dockerfile.dev # single stage with hot reload.The production backend image:
- Stage 1 installs dev deps and runs
tsc. - Stage 2 installs production deps only.
- Stage 3 (
node:24-alpine) copies builtdist/andnode_modules, runs as a non-rootappuser.
The production frontend image:
- Stage 1 installs deps.
- Stage 2 runs
next build. Standalone output is enabled bycreateNextConfig. - Stage 3 copies the standalone server and static assets, runs as a non-root
nextjsuser.
Building a single app image
From the app root:
docker build -f backend/Dockerfile -t my-app-backend .
docker build -f frontend/Dockerfile -t my-app-frontend ./frontendThe backend build context is the app root (it needs package.json, migrations/, and the source). The frontend build context is the frontend/ folder.
Compose files
The generated app ships two compose files:
docker-compose.ymldescribes the production stack: Postgres 18, PostgREST 14, the backend image, the frontend image, a private bridge network.docker-compose.dev.ymlis an override: bind-mounts the source, usesDockerfile.dev, and setsNODE_ENV=development.
Per-app Makefile
The generated Makefile wraps both compose files with the right -f flags so you do not have to type them. From the app root:
make dev-up # start dev stack with hot reload
make dev-down # stop
make dev-build # rebuild dev images
make dev-restart # recreate containers (no rebuild)
make dev-reset # stop and wipe volumes
make dev-logs # tail all logs
make health # check API, frontend, postgresProduction equivalents drop the dev- prefix:
make up
make down
make build
make reset # wipe volumes
make logsBoth stacks publish:
- API on
BACKEND_PORT(default 3000). - Frontend on
FRONTEND_PORT(default 3001).
PostgREST is never published.
If you would rather call docker directly, the Makefile is a thin wrapper; everything it does is docker compose -p crudy-<app> -f docker-compose.yml [-f docker-compose.dev.yml] <subcommand>.
Dev workflow
In dev, the bind mount means edits to backend/server.ts or frontend/app/* reload without a rebuild. The backend uses tsx watch; the frontend uses Next.js dev mode with file polling (WATCHPACK_POLLING=true) for filesystem compatibility inside Docker.
make dev-up
# edit source ...
# changes are picked up automaticallyAfter a database schema change in a module migration or migrations/:
make dev-reset
make dev-upThis wipes the volumes and restarts the stack. The backend re-runs all core and per-app migrations on boot against the fresh database.
make dev-reset destroys all data in your dev database. It is a dev-only command. Do not run it against a production stack.