# 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.