--- # quotesdb-4tec title: '[TRIAGE] D1 migrations in OpenTofu — null_resource local-exec vs separate wrangler step vs manual' status: completed type: task priority: critical created_at: 2026-03-10T23:32:07Z updated_at: 2026-03-10T23:32:07Z --- This is a triage decision ticket. It must be resolved before dependent implementation tickets can proceed. D1 migrations in OpenTofu: how do we apply the SQL schema to a newly created D1 database? Options are a null_resource local-exec in OpenTofu, a separate wrangler d1 execute step, or a manual migration step. 1. **null_resource local-exec** — run `wrangler d1 execute` as a provisioner in OpenTofu. Ties infra and schema together in one `tofu apply`. 2. **Separate wrangler step** — document as a manual step after `tofu apply`. Simpler OpenTofu config, slightly more manual. 3. **API startup migration** — the API runs `CREATE TABLE IF NOT EXISTS` on startup. Works but risks schema drift in production. **Option 2: Separate wrangler step.** - **null_resource local-exec rejected:** `null_resource` provisioners are an OpenTofu anti-pattern. They don't re-run unless tainted, aren't tracked in state, require wrangler installed on the CI runner at `tofu apply` time, and break idempotency. The convenience of a single command is not worth the coupling. - **API startup migration rejected:** Cloudflare Workers spin up per-request via V8 isolates. There is no persistent startup phase. Running DDL (`CREATE TABLE IF NOT EXISTS`) before every request adds latency and is fragile. The Workers fetch handler (D1Repository, wasm32 path) does NOT run migrations. This is only viable for the native/local dev path (rusqlite), where `NativeRepository::run_migrations()` is called once at `main()` startup. - **Separate wrangler step chosen:** This is Cloudflare's canonical approach. The schema SQL lives at `infra/schema.sql` (ticket bb1514). After `tofu apply`, run once: ```sh wrangler d1 execute quotesdb --file infra/schema.sql --remote ``` Idempotent with `CREATE TABLE IF NOT EXISTS`. Integrates cleanly into CI/CD as a post-apply step. Keeps OpenTofu focused on infrastructure, not data. **Note:** TRIAGE 580e66 asks the same question from the Workers runtime angle and arrives at the same answer. Both are now resolved together. - Ticket d0da0b updated: constraint clarified (wrangler step, no null_resource). - Ticket a5049d updated: migration strategy constraint updated (580e66 also resolved). - Ticket 75489a updated: dependency on bb1514 added; goal updated to reference infra/schema.sql. - Ticket 580e66 resolved as co-decided. - New ticket bb1514 created: full implementation plan for `infra/schema.sql`. `chore(quotesdb): resolve triage — d1-migrations-in-opentofu-nullresource-localexec-vs-separate`