Skip to content

feat(registry/bpmct/templates/railway): add Railway (via GraphQL) template#954

Merged
DevelopmentCats merged 6 commits into
mainfrom
bpmct/railway-template
Jul 9, 2026
Merged

feat(registry/bpmct/templates/railway): add Railway (via GraphQL) template#954
DevelopmentCats merged 6 commits into
mainfrom
bpmct/railway-template

Conversation

@bpmct

@bpmct bpmct commented Jul 9, 2026

Copy link
Copy Markdown
Member

Description

Adds a Railway-backed Coder template under registry/bpmct/templates/railway. Each workspace becomes a fully isolated Railway project (own environment, own volume, own service token), running a container built from a pre-built image on GHCR.

What makes this different from what you'd get with the community terraform-community-providers/railway provider:

  • Direct GraphQL, not the provider. The provider hits two blocking issues under stress: railway_volume writes to a service before the service is fully attached (and Railway rejects volumeCreate on services that have had deployments), and variableUpsert triggers extra redeploys that intermittently fail with "Cannot redeploy without a snapshot" / "Cannot redeploy yet, please wait for the original deployment to finish building". This template bypasses the provider and calls the Railway public API directly with curl.
  • skipDeploys: true on variableUpsert. Undocumented flag that persists the value without enqueuing a redeploy. serviceInstanceDeployV2 is what actually deploys, and picks up the freshly-set variables.
  • serviceInstanceUpdate(source: { image }) + serviceInstanceDeployV2 for the image deploy, rather than serviceConnect (which builds from a repo on every start). Median start time drops from ~62s (repo build) to ~6s (pre-built image).
  • Explicit volumeCreate before any deployment activity, with backoff on the "creating volumes too quickly" rate limit.

Full write-up, benchmark suite, and three other approaches (Terraform-provider-based, hybrid, Railway-CLI-based) live at bpmct/coder-railway. The variants/wip/image/ directory in that repo is the exact source this template was extracted from.

Demo (Loom, ~5 min)

Demo

Note

The Loom was recorded before startup was optimized (per-start Docker build, ~62s median). This template now uses serviceInstanceUpdate(source: { image }) against a pre-built image, so median start time is ~6s. The rest of the flow in the demo (workspace creation, project isolation, Railway CLI) still applies.

Type of Change

  • New module
  • New template
  • Bug fix
  • Feature/enhancement
  • Documentation
  • Other

Template Information

Path: registry/bpmct/templates/railway

Testing & Validation

  • Tests pass (bun test)
  • Code formatted (bun fmt)
  • Changes tested locally

Pushed and running on a Railway-hosted Coder deployment (coder.up.railway.app) as template railway-optimized. Bench numbers against that deployment with the default public image (ghcr.io/bpmct/railway-coder-workspace:latest, no registry credentials):

  • Stress: 25/25 concurrent workspace starts (5 iterations of 5 concurrent).
  • Create: 5/5 workspace creates from scratch (project + service + volume + first deploy).
  • Start (median): ~6s from coder start to agent connected.
  • Create (median): ~9s from coder create to first agent connect.

The reliability suite that produced those numbers (parallel start stress test, create stress test, provider-error-scanner) lives at bpmct/coder-railway/scripts/.

Related Issues

None.

File layout
.icons/railway.svg                                    # new (no railway icon existed)
registry/bpmct/templates/railway/
├── README.md                                         # frontmatter + Loom + reasoning + usage + extend guidance
├── main.tf                                           # wires TF resources to scripts/*.sh
├── build/
│   ├── Dockerfile                                    # FROM codercom/enterprise-base:ubuntu + entrypoint
│   └── entrypoint.sh                                 # Railway volume chown + CODER_INIT_SCRIPT_B64 decode + drop to coder
└── scripts/
    ├── lib.sh                                        # shared: gql(), lookup_*(), load_state()
    ├── project_create.sh / project_destroy.sh
    ├── service_create.sh / service_destroy.sh
    ├── volume_create.sh
    ├── env_vars_create.sh / env_vars_destroy.sh      # variableUpsert with skipDeploys: true
    ├── image_deploy_create.sh / image_deploy_destroy.sh  # serviceInstanceUpdate + deployV2
    └── project_token_create.sh / project_token_destroy.sh

Bash bodies live in scripts/*.sh, one file per (resource, action). main.tf only wires command = "bash ${path.module}/scripts/X.sh" and passes runtime values via environment {} blocks. Destroy provisioners reference self.input.* because vars/locals/data are not allowed in destroy scopes.


🤖 Generated with Coder Agents using Claude, on behalf of @bpmct

bpmct added 2 commits July 9, 2026 14:10
…plate

Adds a Railway-backed Coder template that provisions each workspace as
its own Railway project via direct GraphQL calls (no community
Terraform provider). Uses a pre-built image on GHCR and
serviceInstanceUpdate(source: { image }) + serviceInstanceDeployV2 to
keep median start time at ~6s.

See https://github.com/bpmct/coder-railway for the reasoning behind
GraphQL over the Terraform provider and the reliability suite (three
other variants tried along the way).

🤖 Generated with Coder Agents using Claude, on behalf of @bpmct
- Reformats 10 shell scripts to prettier-plugin-sh style (multi-line
  brace groups, spacing around redirects, aligned pipes).
- Replaces `<your-railway-token>` with `YOUR_RAILWAY_TOKEN` in
  README code fences. Angle brackets were being parsed as HTML tags
  by prettier and mangled inside sh fences with line continuations.

🤖 Generated with Coder Agents using Claude, on behalf of @bpmct
@bpmct bpmct marked this pull request as draft July 9, 2026 14:18
Copies build/Dockerfile and build/entrypoint.sh from
bpmct/coder-railway into the template so users don't have to leave
the registry to inspect the image contract.

README's 'Custom workspace image' section now spells out two
extend patterns:

- Extend the default image (FROM ghcr.io/bpmct/railway-coder-workspace:latest,
  layer tools on top). This is the recommended path for most teams
  since it inherits the entrypoint + Coder agent wiring.
- Duplicate build/ and swap the base image entirely. Copy entrypoint.sh
  verbatim, since the volume-chown + CODER_INIT_SCRIPT_B64 decode +
  drop-to-coder contract lives there, not in the base image.

🤖 Generated with Coder Agents using Claude, on behalf of @bpmct
@bpmct bpmct marked this pull request as ready for review July 9, 2026 14:35
@DevelopmentCats DevelopmentCats self-requested a review July 9, 2026 16:56
Comment thread registry/bpmct/templates/railway/main.tf Outdated
Comment thread registry/bpmct/templates/railway/README.md Outdated
@DevelopmentCats

Copy link
Copy Markdown
Collaborator

@bpmct Everything works well for me as well. I just wanted to test it as well because I haven't had a chance to mess with railway much but I picked out a few things maybe worth changing before we commit but otherwise LGTM!

bpmct and others added 2 commits July 9, 2026 15:04
Co-authored-by: DevCats <christofer@coder.com>
Co-authored-by: DevCats <christofer@coder.com>
@bpmct bpmct requested a review from DevelopmentCats July 9, 2026 19:05
@DevelopmentCats DevelopmentCats merged commit f971ec6 into main Jul 9, 2026
4 checks passed
@DevelopmentCats DevelopmentCats deleted the bpmct/railway-template branch July 9, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants