Skip to content

Ask AI

Ask anything about Destesi — setup, products, APIs.

Powered by Claude. Answers may be wrong — always verify against the docs.

Compose file

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.

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.

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.

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.
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: 40

No db service, no depends_on, no top-level volumes:. Snap provides the database; the overlay provides the routing.

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 a
docker-compose.destesi.yml for Destesi Preview. Destesi previews are
ephemeral and the database is provided by a Snap fork, not by a container
in 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.

Destesi’s runner picks the first compose file it finds, in this order:

  1. docker-compose.destesi.yml — preview-specific, wins whenever present.
  2. docker-compose.dev.yml — legacy preview path; bind-mounted source, no DB stripping.
  3. 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.

  • Running it locally is expected to fail. docker compose -f docker-compose.destesi.yml up has no DB and no resolved ${DATABASE_URL}. That’s the file advertising its intent — it’s preview-only. Use docker-compose.yml for local dev.
  • Redis / Kafka / other state services aren’t forked by Snap today. Keep those services in .destesi.yml for 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 of migrate 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.