Add SAAC_DEPLOYMENT.md - deployment rules for AI agents

This commit is contained in:
2026-02-16 18:00:48 +00:00
parent a9487484fe
commit 6933097afd

141
SAAC_DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,141 @@
# SAAC Deployment — How Your App Gets Built
> **READ THIS FIRST.** This file explains exactly how SAAC deploys your app.
> Understanding this will save you hours of debugging.
## The Build Process
When you run `saac deploy`, the daemon executes these exact commands in your repo:
```bash
docker compose -f docker-compose.yml -f docker-compose.saac.yml build
docker compose -p saac-{uuid} -f docker-compose.yml -f docker-compose.saac.yml up -d
```
**Your `docker-compose.yml` and `Dockerfile` ARE used.** The auto-generated `docker-compose.saac.yml` overlay ADDS labels, networks, and restart policy — it never replaces your config.
## Required Files
Your repo MUST have:
1. `docker-compose.yml` — defines services (app, postgres, redis)
2. `Dockerfile` — referenced by the build directive in compose
## The 5 Rules
### Rule 1: Use `expose`, NEVER `ports`
Traefik reverse proxy handles all external routing. Host port bindings conflict with other apps.
```yaml
# WRONG — will conflict with other apps on the server
ports:
- "3000:3000"
# CORRECT — Traefik routes traffic to this port
expose:
- "3000"
```
### Rule 2: Database host = service name, NOT localhost
In Docker Compose, services talk to each other by service name.
```yaml
# WRONG — localhost means "inside this container" in Docker
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/mydb
# CORRECT — "postgres" is the service name in docker-compose.yml
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/mydb
# ALSO CORRECT — if using shared org database
DATABASE_URL: postgresql://postgres:postgres@postgres.internal:5432/mydb
```
### Rule 3: Dockerfile must produce a running container
```dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# If TypeScript: RUN npm run build
CMD ["node", "server.js"]
```
Key points:
- `npm install` happens INSIDE the container (not on host)
- `COPY . .` copies your source into the container
- `CMD` must start your app
- For TypeScript: add `RUN npm run build` before CMD, use `CMD ["node", "dist/server.js"]`
### Rule 4: Keep it simple — iterate incrementally
**Start with the working template, then add features one at a time.**
DO NOT:
- Replace Express with a full TypeScript/Prisma/monorepo stack in one commit
- Create complex multi-stage Dockerfiles before the basic app works
- Add workspace configurations before single-service works
- Change the directory structure before deploying successfully once
DO:
- Get the template deploying first (`saac deploy`, verify with `saac logs`)
- Add one feature, deploy, verify
- Add the next feature, deploy, verify
### Rule 5: .dockerignore should NOT exclude build outputs
```
# Good .dockerignore
node_modules
.git
.env
```
Do NOT add `dist/` or `build/` — those are generated INSIDE the container by `RUN npm run build`. The .dockerignore only affects what gets COPIED into the build context.
## Environment Variables
Env vars set via `saac env set KEY=VALUE` are written to `.env` in your repo directory before build. Reference them in docker-compose.yml:
```yaml
environment:
- DATABASE_URL=${DATABASE_URL}
```
Or use `env_file: .env` on your service.
## Two Containers Per App
Every app gets:
- **Production** (`yourapp.startanaicompany.com`) — immutable image, rebuilt on `saac deploy`
- **Hot-reload** (`yourapp-hot.startanaicompany.com`) — volume-mounted source, auto-updates on `git push`
## Debugging Commands
```bash
saac logs # Runtime logs
saac logs --type build # Build/deploy logs
saac exec "ls -la" # Run command in container
saac exec "cat package.json" # Check what's actually in the container
saac db sql "SELECT * FROM users LIMIT 5" # Query database
saac db containers # List database containers
```
## Common Mistakes and Fixes
| Mistake | Fix |
|---------|-----|
| `ports: "3000:3000"` | Change to `expose: ["3000"]` |
| `DB_HOST=localhost` | Change to `DB_HOST=postgres` (service name) |
| No Dockerfile | Create one (see Rule 3) |
| TypeScript not compiling | Add `RUN npm run build` in Dockerfile |
| `.dockerignore` has `dist/` | Remove it — dist is built inside container |
| `npm start` undefined | Add `"start": "node server.js"` to package.json scripts |
| Container exits immediately | Check `saac logs` — usually a missing env var or import error |
| "ECONNREFUSED 127.0.0.1" | You're using localhost — change to the Docker service name |
## Do NOT Add Traefik Labels
The SAAC daemon handles Traefik routing automatically via file provider. Traefik Docker labels in your docker-compose.yml are **ignored**. You can remove them.