Compose file
docker-compose.destesi.yml
Section titled “docker-compose.destesi.yml”The preview-specific compose file. Destesi runs this in every preview pod — when it exists, it wins over docker-compose.dev.yml and docker-compose.yml. Author one so your preview stops starting a throwaway database container next to the Snap fork.
Why a separate file?
Section titled “Why a separate file?”Your local dev compose probably ships a db service so docker compose up works offline. In a preview that container is wasted work — Destesi provisions a write-isolated Snap fork of Postgres and injects DATABASE_URL pointing at it. Starting a second Postgres inside the pod:
- Wastes pod memory and adds 5-10s to startup.
- Hides bugs where the app accidentally hits the local container instead of the Snap fork.
- Forces you to pick between “my compose works locally” and “my compose works in a preview” when the two shapes differ.
A preview-only compose file solves both: docker-compose.yml keeps working for local dev; docker-compose.destesi.yml is what the preview runner picks up.
1. Prepare your preview env
Section titled “1. Prepare your preview env”Before writing the file, make sure the preview’s env inputs are wired. The compose file assumes these exist — it doesn’t provide defaults.
| Prerequisite | How |
|---|---|
| A Snap remote to fork from | dst snap remote postgres <dsn>, then pass --from-remote <name-or-id> on dst preview create/upsert. See Quickstart. |
DATABASE_URL declared as a preview env |
--env 'DATABASE_URL=${postgres.dsn}' on dst preview upsert. See Env templates. |
App secrets (NEXTAUTH_SECRET, etc.) |
--env-secret NAME=value — values are encrypted at rest and stay redacted in the dashboard and API responses. |
| OAuth providers (if any) | --oauth google=/api/auth/callback/google. See OAuth passthrough. Do not set NEXTAUTH_URL in the compose file — the overlay handles it. |
The compose file references these via ${VAR} interpolation; Destesi’s runner fans the resolved values into every service via a generated override, so no per-service environment: blocks are needed.
2. What the file needs
Section titled “2. What the file needs”Keep:
- Your app service(s).
- Bind mounts for hot reload (
./:/app, etc.) — the preview agent’s edits need to appear without a rebuild. - Your app’s own healthcheck — the runner uses it to decide when the preview is
healthy. - Non-DB sidecars your app needs (queues, caches the app connects to externally).
${DATABASE_URL}interpolation so the Snap-forked DSN reaches the app.
Strip:
- Database services (
postgres,mysql, etc.) that Snap replaces. - Their named volumes (
db_data, etc.). depends_on:entries pointing at stripped services.- Healthchecks that waited on the stripped DB.
- Hardcoded
DATABASE_URL=postgres://...@db:5432/...— replace with${DATABASE_URL}. - Hardcoded
NEXTAUTH_URL— the overlay proxy sets it per-preview.
Example (Next.js + Auth.js + Prisma)
Section titled “Example (Next.js + Auth.js + Prisma)”services: app: image: node:22-alpine working_dir: /app command: sh -c "apk add --no-cache openssl && npm ci --legacy-peer-deps && npx prisma db push --skip-generate --accept-data-loss && exec npm run dev" ports: - "8080:3000" environment: DATABASE_URL: ${DATABASE_URL} # injected by Destesi from the Snap fork NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-dev-secret-change-me} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} NEXT_TELEMETRY_DISABLED: "1" volumes: - ./:/app - /app/node_modules - /app/.next healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/api/health"] interval: 5s timeout: 3s retries: 40No db service, no depends_on, no top-level volumes:. Snap provides the database; the overlay provides the routing.
3. Generate it with Claude / Cursor
Section titled “3. Generate it with Claude / Cursor”Copy this block into your AI of choice and paste your existing docker-compose.yml (or docker-compose.dev.yml) under it. The output is a first-draft docker-compose.destesi.yml you can tweak by hand.
You are converting a docker-compose.yml (or docker-compose.dev.yml) into adocker-compose.destesi.yml for Destesi Preview. Destesi previews areephemeral and the database is provided by a Snap fork, not by a containerin the compose file. Follow these rules:
1. Remove every database service (Postgres / MySQL / etc. that your app normally forks in the preview). Remove their depends_on references, their healthchecks, and their named volumes. Remove the top-level "volumes:" block if it only held those.2. Replace any hardcoded DATABASE_URL (or equivalent: DB_URL, POSTGRES_URL, etc.) with ${DATABASE_URL}. Destesi injects this env var from the Snap fork at runtime.3. Remove any hardcoded NEXTAUTH_URL / AUTH_URL. Destesi's overlay proxy sets these per-preview via the forwarded host.4. Keep bind mounts for hot reload (./:/app, etc.) so the preview-agent's edits appear without a rebuild.5. Keep the app's own healthcheck — the runner uses it to decide when the preview reaches "healthy".6. Keep non-DB services (queues, caches, internal sidecars the app speaks to). Only remove or rewrite; do not add new services.7. Output only the new docker-compose.destesi.yml — no commentary.
Paste the source compose below.Check the output against the keep/strip rules above. The prompt is conservative — if your app has a non-obvious embedded DB (e.g. a backend service that internally runs sqlite/postgres), the AI will leave it alone, which is usually what you want.
Precedence
Section titled “Precedence”Destesi’s runner picks the first compose file it finds, in this order:
docker-compose.destesi.yml— preview-specific, wins whenever present.docker-compose.dev.yml— legacy preview path; bind-mounted source, no DB stripping.docker-compose.yml— fallback.
Local docker compose up still uses docker-compose.yml (or -f docker-compose.dev.yml) unchanged — Destesi never reads them unless .destesi.yml is absent.
Gotchas
Section titled “Gotchas”- Running it locally is expected to fail.
docker compose -f docker-compose.destesi.yml uphas no DB and no resolved${DATABASE_URL}. That’s the file advertising its intent — it’s preview-only. Usedocker-compose.ymlfor local dev. - Redis / Kafka / other state services aren’t forked by Snap today. Keep those services in
.destesi.ymlfor now — they’ll run per-preview and be discarded with the pod. - Migrations. The Snap fork inherits the parent branch’s schema, so use
prisma db push(or equivalent idempotent sync) instead ofmigrate deploy, which errors when tables already exist. - The preview agent refuses compose edits. By design — when a change belongs in
.destesi.yml, the agent will point you at it, but the edit itself goes through a normal PR.