a month ago
We have existing, never-deployed services with no GitHub source or deployment triggers.
We need to connect a GitHub repository at a pinned full commit SHA while guaranteeing both zero initial deployments and disabled future push autodeploy from the instant of connection.
The services include a migration runner, so connecting and disabling afterward is unsuitable.
Does setting serviceInstanceAutoDeployUpdate(enabled:false) before a source exists persist through source binding, including when status currently reports NO_REPO? If so, which source mutation preserves that flag? Alternatively, is there an atomic source-binding API or staged config field that disables trigger creation? Please give the supported sequence and readback fields proving the effective state. Does environmentPatchCommitStaged(skipDeploys:true) only suppress the immediate deploy, or also affect push-trigger creation? What does branch:null mean when repo and commitSha are supplied?
3 Replies
a month ago
This thread has been opened as a bounty so the community can help solve it.
Status changed to Open Railway • 27 days ago
a month ago
Short version: Railway separates source binding, push triggers, and deploys into
three independent surfaces. Once you see that, most of this resolves — and one of
your premises doesn't hold.
- branch:null with repo + commitSha
No mutation accepts repo and commitSha together, so this case doesn't arise.
ServiceConnectInput is {repo, branch, image}; ServiceSourceInput is {repo, image}.
Neither takes a SHA. commitSha appears only on the deploy mutations:
serviceInstanceDeploy(serviceId, environmentId, commitSha, latestCommit)
serviceInstanceDeployV2(serviceId, environmentId, commitSha)
In ServiceConnectInput, branch is optional (String, not String!). Omitting it means
Railway resolves the repo's default branch. It does not mean "untracked" or "unpinned."
So a pinned SHA is a property of a deploy, never of the binding. Bind the repo, then
deploy the SHA explicitly when you're ready.
- Push autodeploy is a separate object, not a flag on the binding
Push deploys are DeploymentTrigger rows, managed by deploymentTriggerCreate /
deploymentTriggerUpdate / deploymentTriggerDelete, and readable via:
deploymentTriggers(projectId, environmentId, serviceId)
Project.deploymentTriggers / Environment.deploymentTriggers
Your guarantee should come from asserting no DeploymentTrigger exists for the
service instance — not from trusting a boolean set before binding.
- serviceInstanceAutoDeployUpdate before a source exists
I would not rely on it persisting. The status query is:
serviceInstanceAutoDeployStatus(projectId, environmentId, serviceId)
-> { enabled, canEnable, reason }The presence of canEnable and reason indicates the flag is gated on source state —
NO_REPO is a reason for canEnable:false. Treat autoDeploy as derived state you assert
after binding and then verify, rather than a durable pre-set.
- environmentPatchCommitStaged(skipDeploys: true)
Its documented behaviour is "Skip deploys for services affected by this patch" — it
suppresses deploys arising from that patch only. Triggers are created through a
separate mutation (deploymentTriggerCreate), so skipDeploys has no bearing on whether
a push trigger exists. Assert trigger absence separately.
- Sequence I'd use
a. Bind without a branch:
serviceInstanceUpdate(serviceId, environmentId,
input: { source: { repo: "owner/name" } })b. Assert no trigger:
deploymentTriggers(projectId, environmentId, serviceId)
-> deploymentTriggerDelete(id) for anything returnedc. Assert autodeploy off:
serviceInstanceAutoDeployUpdate(
input: { projectId, environmentId, serviceId, enabled: false })d. Read back and prove effective state:
serviceInstance(serviceId, environmentId) {
hasEverDeployed # false proves zero deployments ever occurred
source { repo }
latestDeployment { id } # null
}
deploymentTriggers(projectId, environmentId, serviceId) { edges { node { id } } }
serviceInstanceAutoDeployStatus(projectId, environmentId, serviceId) {
enabled canEnable reason }e. When you actually want the pinned build:
serviceInstanceDeployV2(serviceId, environmentId, commitSha: "<40-char sha>")hasEverDeployed is the field that answers your migration-runner concern directly —
it is a permanent record, so you can verify nothing ever fired rather than inferring
it from deployment list emptiness.
Caveat on what I've verified: the above is from the live GraphQL schema
(introspection shapes, descriptions, and nullability). Two things I have not
confirmed empirically on a live NO_REPO service: whether serviceConnect auto-creates
a DeploymentTrigger as a side effect, and whether a pre-binding autoDeploy:false
survives binding. Step (b) makes both moot by asserting the end state rather than
trusting the transition — which I'd recommend regardless.
a month ago
Thanks. Our requirement is zero deployments during source binding, not just a clean final state. Does serviceInstanceUpdate with source.repo create an initial deployment or deployment trigger on a never-deployed NO_REPO service? Is there a supported operation that guarantees neither side effect? Post-binding deletion is too late for our migration runner.
23 days ago
We have the same requirement for an existing production service: attach a GitHub repo without any initial deployment or temporary autodeploy window.
Can Railway staff confirm whether there is a supported atomic or staged sequence that guarantees this? If not, we'll continue using our manual archive deployment path.