diff --git a/Dockerfile b/Dockerfile index 7cc619b..3bf6416 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,10 @@ -# Multi-stage Dockerfile for React + Express application +# Multi-stage Dockerfile for React + Express application (PRODUCTION container) # See SAAC_DEPLOYMENT.md for deployment rules and common mistakes. # +# This Dockerfile is ONLY used by the production container (yourapp..domain.com). +# The hot-reload container (yourapp-hot..domain.com) does NOT use this file — +# it mounts your source code directly and uses nodemon.json to watch for changes. +# # Stage 1 (builder): Installs client deps and builds the React app with Vite. # Stage 2 (production): Installs server deps only, copies built React output. # diff --git a/SAAC_DEPLOYMENT.md b/SAAC_DEPLOYMENT.md index 69ab0d3..1c2568e 100644 --- a/SAAC_DEPLOYMENT.md +++ b/SAAC_DEPLOYMENT.md @@ -3,6 +3,82 @@ > **READ THIS FIRST.** This file explains exactly how SAAC deploys your app. > Understanding this will save you hours of debugging. +## Your Two Domains + +Every app gets **two live URLs** on the server. Both are created automatically when you deploy: + +``` +yourapp..startanaicompany.com ← PRODUCTION (customers see this) +yourapp-hot..startanaicompany.com ← HOT-RELOAD (you develop with this) +``` + +For example, if your app domain is `my-saas.adam.startanaicompany.com`: +- Production: `https://my-saas.adam.startanaicompany.com` +- Hot-reload: `https://my-saas-hot.adam.startanaicompany.com` + +### How to Use Them + +**Hot-reload (`-hot` domain)** — Use this while developing. Every `git push` automatically: +1. Pulls your latest code +2. Rebuilds the React frontend (`npm run build`) +3. Restarts the Express server +4. Your changes are live in ~10-20 seconds + +**Recommended development workflow:** +```bash +# 1. Make your code changes +# 2. Push to git +git add . && git commit -m "Add login page" && git push + +# 3. Wait ~15 seconds, then check the hot domain +curl https://yourapp-hot.adam.startanaicompany.com/health + +# 4. Once it works on hot, deploy to production +saac deploy + +# 5. Verify production +curl https://yourapp.adam.startanaicompany.com/health +``` + +**Production domain** — Only updates when you run `saac deploy`. This rebuilds the Docker image from your Dockerfile and restarts everything. Use this for the final, tested version. + +### Why Two Containers? + +| Container | Domain | How it runs your code | When it updates | +|-----------|--------|----------------------|-----------------| +| **Production** | `yourapp.example.com` | Built from your `Dockerfile` (immutable Docker image) | Only on `saac deploy` | +| **Hot-reload** | `yourapp-hot.example.com` | Volume-mounts your git repo, runs `npm run dev` via nodemon | Automatically on every `git push` | + +**Key differences:** +- **Production** uses your Dockerfile. The Docker image is built once and runs until next deploy. +- **Hot-reload** does NOT use your Dockerfile. It mounts your source code directly, installs dependencies, builds the React client, and runs Express with nodemon. +- **Hot-reload auto-rebuilds everything** — `nodemon.json` watches both `server.js` and `client/src/`. When files change (after `git push`), it runs `npm run build` (React) and restarts `node server.js`. + +### How nodemon.json Works + +The `nodemon.json` file in your repo tells the hot-reload container what to watch: + +```json +{ + "watch": ["server.js", "client/src"], + "ext": "js,ts,tsx,jsx,css,json", + "exec": "npm run build && node server.js" +} +``` + +- **watch**: Directories/files to monitor for changes +- **ext**: File extensions that trigger a rebuild +- **exec**: What to run when changes are detected — rebuilds React, restarts Express + +**You can customize this.** For example, if you add a `lib/` directory with shared code: +```json +{ + "watch": ["server.js", "client/src", "lib"], + "ext": "js,ts,tsx,jsx,css,json", + "exec": "npm run build && node server.js" +} +``` + ## The Build Process When you run `saac deploy`, the daemon executes these exact commands in your repo: @@ -20,23 +96,6 @@ Your repo MUST have: 1. `docker-compose.yml` — defines services (app, postgres, redis) 2. `Dockerfile` — referenced by the build directive in compose -## CRITICAL: Two Containers Per App - -Every app gets TWO containers. Understanding this prevents the #1 debugging confusion: - -| Container | Domain | How it runs your code | When it updates | -|-----------|--------|----------------------|-----------------| -| **Production** | `yourapp.startanaicompany.com` | Built from your `Dockerfile` (immutable image) | Only on `saac deploy` | -| **Hot-reload** | `yourapp-hot.startanaicompany.com` | Volume-mounts your git repo, runs `npm run dev` | Instantly on `git push` | - -**Key facts:** -- **Production** runs the Docker image you built. Your Dockerfile, CMD, and build steps all apply. -- **Hot-reload** does NOT use your Dockerfile. It mounts your source code directly and runs nodemon/npm run dev. -- **Your main domain routes to PRODUCTION, not hot-reload.** If production works but hot-reload doesn't, your app is fine. -- **TypeScript projects**: Production container compiles TS in the Dockerfile (`RUN npm run build`). Hot-reload needs a `dev` script in package.json that compiles and runs (e.g., `"dev": "tsc && node dist/server.js"` or `"dev": "tsx watch src/server.ts"`). - -**Common confusion:** "My Docker build works but the app serves old code!" — You're probably looking at the hot-reload container. Check the PRODUCTION domain (without `-hot`). - ## The 5 Rules ### Rule 1: Use `expose`, NEVER `ports` @@ -93,8 +152,8 @@ DO NOT: DO: - Get the template deploying first (`saac deploy`, verify with `saac logs`) -- Add one feature, deploy, verify -- Add the next feature, deploy, verify +- Add one feature, push, check the hot domain +- When it works, add the next feature - **Coordinate deploys** — only ONE agent should push/deploy at a time ### Rule 5: Dockerfile must produce a running container @@ -135,6 +194,22 @@ saac exec "cat package.json" # Check what's in the container saac db sql "SELECT * FROM users LIMIT 5" # Query database ``` +## Quick Reference: Development Cycle + +``` + git push ──→ hot-reload updates (~15s) ──→ check yourapp-hot domain + │ │ + │ ┌─────── works? ──┘ + │ │ + │ YES ────┤──── NO + │ │ │ │ + │ saac deploy │ fix code, git push again + │ │ │ + │ production │ + │ updated │ + └────────────────────────────┘ +``` + ## Common Mistakes and Fixes | Mistake | Fix | @@ -145,8 +220,9 @@ saac db sql "SELECT * FROM users LIMIT 5" # Query database | `tsc: not found` in Dockerfile | Install ALL deps first: `RUN npm install` (not `--production`) | | `.dockerignore` has `dist/` | Remove it — dist is built inside container | | Multiple agents deploying simultaneously | Coordinate — one agent deploys at a time | -| "App serves old code" | Check production domain (not hot-reload). Run `saac deploy` to rebuild. | -| Hot-reload container crashes | This is separate from production. Fix `package.json` `dev` script. | +| "App serves old code" on production | Run `saac deploy` to rebuild the Docker image | +| "App serves old code" on hot domain | Wait ~15s after push. Check `saac logs` for rebuild errors | +| Hot-reload container crashes | Check `saac logs` — usually a missing dependency or build error | ## Do NOT Add Traefik Labels diff --git a/docker-compose.yml b/docker-compose.yml index 03b499d..69fe543 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,10 @@ -# SAAC Application Stack +# SAAC Application Stack — PRODUCTION container # See SAAC_DEPLOYMENT.md for full deployment rules and common mistakes. # +# This file is used by "saac deploy" to build the production Docker image. +# Your app also gets a HOT-RELOAD container (yourapp-hot..domain.com) +# that auto-rebuilds on every git push — see nodemon.json for its config. +# # Key rules: # 1. Use "expose", NEVER "ports" — Traefik handles routing # 2. Database host = service name ("postgres"), NEVER "localhost"