You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Railway PR previews fail often, and every failure has needed its own patch. The root problem is architectural, not any single bug: each preview constructs a full deployment from nothing (1 project, 13 services, 3 volumes, ~100 variables, a domain) by issuing roughly 55-75 Railway CLI calls plus ~17 GraphQL calls, all non-transactional, many non-idempotent with ambiguous timeouts, against one shared account token. With that many remote writes per run, some run always fails, and a half-created environment cannot self-heal because "Re-run failed jobs" skips the green setup job. We have accumulated 22 distinct patched failure modes in hosting/railway (service-add ambiguity #5566, volume throttling, project-list races #4589, token confusion #4392, read-after-write races #5322, rate limits, and more). PR #5576 was the latest attempt to patch one of them.
Proposed change: clone, don't construct
Railway duplicates a fully configured environment in one call (environmentCreate with sourceEnvironmentId; CLI railway environment new --duplicate), copying services, variables, and settings. Railway's own documented CI recipe for PR previews is exactly this: clone on PR open, delete on PR close.
Target shape:
One always-on, fully configured template environment (13 services from GHCR images, volumes, variables, healthchecks). We evolve it once, through review, not on every PR.
Per PR: one clone call, then patch the 4 app image tags to the PR's GHCR tags (serviceInstanceUpdate + serviceInstanceDeployV2 each), run migrations and tests, and one environmentDelete on close.
Per-PR Railway mutations drop from ~75 to ~10-12. bootstrap.sh and most of configure.sh retire, and most of the historical failure catalog attaches to calls that no longer exist.
Prerequisite: the wrapper Dockerfiles that deploy-from-images.sh generates at deploy time (redis chown, seaweedfs IAM, nginx config, venv paths) move into the build workflow so GHCR images are self-contained and swapping the tag is the entire per-PR change.
Change management for the template
The template definition lives in the repo as versioned code (serialized config + an apply script), not as hand-clicked dashboard state. Changes are PRs.
App code and wrapper changes never touch the template; they ride the per-PR image tag. The template changes only for topology and config shape: add/remove/rename a service, add/remove a required variable, volume or mount changes, healthcheck/networking/domain changes, infra image bumps (postgres, redis, seaweedfs, supertokens), Railway settings.
Testing a template change: the PR clones the current template, applies the definition delta to the clone, and runs the normal deploy + smoke + tests against it. On merge, CI applies the definition to the live template environment.
Ordering rule, same discipline as DB migrations: additive template changes merge and apply first, then the code PR that depends on them. Template changes must stay backward compatible with the newest images on main.
Drift guard: a scheduled job diffs the live template environment against the versioned definition and fails loudly on hand-edits.
Plan
Spike (in a throwaway Railway project, no workflow changes): hand-build the template environment, then script the full cycle clone → patch image tags → deploy → smoke → delete, driving GraphQL directly. Success: 10 consecutive green cycles, ≤15 API calls per cycle, wall time at or below today's setup+deploy. Must answer with evidence: 504-then-poll behavior at 13-service scale, clone fidelity (reference variables, volumes, domains, private networking), whether template services can stay undeployed to save cost, and that the flow authenticates from a GitHub Actions PR job (account-token requirement; fork PRs stay unsupported as today).
Phase 1: publish self-contained wrapper images from the build workflow (also kills the wrapper-vs-compose drift failure family).
Phase 3: swap workflows 41/43/45 to the new flow; retire bootstrap.sh and most of configure.sh.
Change-management protocol live: template-change test flow and drift guard.
Known risks: the GraphQL API is effectively the dashboard's API and not contract-stable; environmentCreate has a documented history of 504-while-succeeding; sealed variables do not copy; per-preview secrets need an explicit overwrite list; the template environment needs an owner. Fallback if clone fidelity fails: templateDeployV2 with a serialized config we control.
Design workspace with the full failure catalog and platform research: docs/design/railway-preview-clone-spike/.
Railway PR previews fail often, and every failure has needed its own patch. The root problem is architectural, not any single bug: each preview constructs a full deployment from nothing (1 project, 13 services, 3 volumes, ~100 variables, a domain) by issuing roughly 55-75 Railway CLI calls plus ~17 GraphQL calls, all non-transactional, many non-idempotent with ambiguous timeouts, against one shared account token. With that many remote writes per run, some run always fails, and a half-created environment cannot self-heal because "Re-run failed jobs" skips the green setup job. We have accumulated 22 distinct patched failure modes in
hosting/railway(service-add ambiguity #5566, volume throttling, project-list races #4589, token confusion #4392, read-after-write races #5322, rate limits, and more). PR #5576 was the latest attempt to patch one of them.Proposed change: clone, don't construct
Railway duplicates a fully configured environment in one call (
environmentCreatewithsourceEnvironmentId; CLIrailway environment new --duplicate), copying services, variables, and settings. Railway's own documented CI recipe for PR previews is exactly this: clone on PR open, delete on PR close.Target shape:
serviceInstanceUpdate+serviceInstanceDeployV2each), run migrations and tests, and oneenvironmentDeleteon close.bootstrap.shand most ofconfigure.shretire, and most of the historical failure catalog attaches to calls that no longer exist.Prerequisite: the wrapper Dockerfiles that
deploy-from-images.shgenerates at deploy time (redis chown, seaweedfs IAM, nginx config, venv paths) move into the build workflow so GHCR images are self-contained and swapping the tag is the entire per-PR change.Change management for the template
Plan
bootstrap.shand most ofconfigure.sh.Known risks: the GraphQL API is effectively the dashboard's API and not contract-stable;
environmentCreatehas a documented history of 504-while-succeeding; sealed variables do not copy; per-preview secrets need an explicit overwrite list; the template environment needs an owner. Fallback if clone fidelity fails:templateDeployV2with a serialized config we control.Design workspace with the full failure catalog and platform research:
docs/design/railway-preview-clone-spike/.