a month ago
I am trying to switch my setup from web defined to the new IaC (with python) and railway config plan shows
~ Update Postgres deploy.sleepApplication
└ deploy.sleepApplication (true → null)
I am trying to use the following setup
Postgres = postgres(
"Postgres",
region="sfo",
deploy={
"sleepApplication": True,
},
)
Pinned Solution
a month ago
deploy= isn't wired up on the database helpers, so your dict never reaches the graph. That's why the plan shows a removal instead of a no-op: the compiled node has no deploy.sleepApplication at all, so diffing it against the environment (where the dashboard already set it to true) comes out as true -> null.
postgres() is a thin wrapper over database(), and database() cherry-picks options by name:
def database(name: str, engine: str, **options: Any) -> Service:
image_name = options["image"]
output = options.get("output") or "DATABASE_URL"
node: dict[str, Any] = { ...address / type / kind / engine / name / image / output / source... }
if options.get("defaultMountPath"):
node["defaultMountPath"] = options["defaultMountPath"]
if options.get("region"):
node["deploy"] = {"multiRegionConfig": {options["region"]: {"numReplicas": 1}}}
return Service(node)Only image, output, defaultMountPath and region are ever read. Anything else is swallowed by **options and dropped without a warning. service() goes through a different path (_service_node -> _normalize_deploy) which does merge deploy, which is why the same dict works there and not here.
Your call is doubly unlucky: region="sfo" assigns node["deploy"] outright, so even if deploy were merged in, the region branch would overwrite it. Both kwargs land on the same key.
Compiling the node shows it (railway-sdk, railwayapp/railway-py-sdk @ e2df8d4):
\>>> postgres("Postgres", region="sfo", deploy={"sleepApplication": True}).to_graph()["deploy"]
{'multiRegionConfig': {'sfo': {'numReplicas': 1}}}
\>>> "deploy" in postgres("Postgres", deploy={"sleepApplication": True}).to_graph()
False
\>>> service("web", source=github("org/app"), deploy={"sleepApplication": True}).to_graph()["deploy"]
{'sleepApplication': True}Until the helper takes it, set the field on the node yourself. with_fields() is a public method on the returned Service:
Postgres = postgres("Postgres").with_fields(
deploy={
"multiRegionConfig": {"sfo": {"numReplicas": 1}},
"sleepApplication": True,
}
)with_fields shallow-merges onto the node, so it replaces deploy wholesale. That's why the region has to be written back by hand, and why you want to drop region="sfo" from the postgres() call rather than leave both in and depend on which one runs last. Result:
{'multiRegionConfig': {'sfo': {'numReplicas': 1}}, 'sleepApplication': True}Same shape service() would have produced, so the plan should stop trying to unset the field.
Probably worth an issue on railwayapp/railway-py-sdk too. The helpers accept **config and silently discard whatever they don't recognise, so any typo or unsupported key fails exactly like this: no error, and a plan that quietly reverts something you set in the dashboard.
8 Replies
a month ago
This thread has been opened as a bounty so the community can help solve it.
Status changed to Open Railway • about 1 month ago
a month ago
deploy= isn't wired up on the database helpers, so your dict never reaches the graph. That's why the plan shows a removal instead of a no-op: the compiled node has no deploy.sleepApplication at all, so diffing it against the environment (where the dashboard already set it to true) comes out as true -> null.
postgres() is a thin wrapper over database(), and database() cherry-picks options by name:
def database(name: str, engine: str, **options: Any) -> Service:
image_name = options["image"]
output = options.get("output") or "DATABASE_URL"
node: dict[str, Any] = { ...address / type / kind / engine / name / image / output / source... }
if options.get("defaultMountPath"):
node["defaultMountPath"] = options["defaultMountPath"]
if options.get("region"):
node["deploy"] = {"multiRegionConfig": {options["region"]: {"numReplicas": 1}}}
return Service(node)Only image, output, defaultMountPath and region are ever read. Anything else is swallowed by **options and dropped without a warning. service() goes through a different path (_service_node -> _normalize_deploy) which does merge deploy, which is why the same dict works there and not here.
Your call is doubly unlucky: region="sfo" assigns node["deploy"] outright, so even if deploy were merged in, the region branch would overwrite it. Both kwargs land on the same key.
Compiling the node shows it (railway-sdk, railwayapp/railway-py-sdk @ e2df8d4):
\>>> postgres("Postgres", region="sfo", deploy={"sleepApplication": True}).to_graph()["deploy"]
{'multiRegionConfig': {'sfo': {'numReplicas': 1}}}
\>>> "deploy" in postgres("Postgres", deploy={"sleepApplication": True}).to_graph()
False
\>>> service("web", source=github("org/app"), deploy={"sleepApplication": True}).to_graph()["deploy"]
{'sleepApplication': True}Until the helper takes it, set the field on the node yourself. with_fields() is a public method on the returned Service:
Postgres = postgres("Postgres").with_fields(
deploy={
"multiRegionConfig": {"sfo": {"numReplicas": 1}},
"sleepApplication": True,
}
)with_fields shallow-merges onto the node, so it replaces deploy wholesale. That's why the region has to be written back by hand, and why you want to drop region="sfo" from the postgres() call rather than leave both in and depend on which one runs last. Result:
{'multiRegionConfig': {'sfo': {'numReplicas': 1}}, 'sleepApplication': True}Same shape service() would have produced, so the plan should stop trying to unset the field.
Probably worth an issue on railwayapp/railway-py-sdk too. The helpers accept **config and silently discard whatever they don't recognise, so any typo or unsupported key fails exactly like this: no error, and a plan that quietly reverts something you set in the dashboard.
matmair
thank you, that worked!
a month ago
Could you please approve my response ?
Thanks :)
arthurveisseire
Could you please approve my response ? Thanks :)
a month ago
i am no emloyee of railway so no
matmair
i am no emloyee of railway so no
a month ago
You don't need to be; there was a simple "Approve" button.
Status changed to Awaiting Conductor Response Railway • about 1 month ago
Status changed to Solved brody • about 1 month ago
brody
You don't need to be; there was a simple "Approve" button.
a month ago
I did that 11h ago and it just changed to showing some unhelpful "awaiting conductor" badge
Status changed to Awaiting Conductor Response Railway • about 1 month ago
Status changed to Awaiting User Response Railway • about 1 month ago
a month ago
than write that in a info bubble or tooltip; these childish train terms are not helpful for grown up engineers that have stuff to do
Status changed to Awaiting Conductor Response Railway • about 1 month ago
Status changed to Solved brody • about 1 month ago
