[{"data":1,"prerenderedAt":357},["ShallowReactive",2],{"blog-how-to/railway-pr-environments":3},{"id":4,"title":5,"body":6,"category":325,"date":326,"dateModified":326,"description":327,"draft":328,"extension":329,"faq":330,"featured":328,"headerVariant":341,"image":342,"keywords":343,"meta":344,"navigation":345,"ogDescription":346,"ogTitle":342,"path":347,"readTime":348,"schemaOrg":349,"schemaType":350,"seo":351,"sitemap":352,"stem":353,"tags":354,"twitterCard":355,"__hash__":356},"blog/blog/how-to/railway-pr-environments.md","Railway PR Environments: What They Copy From Production (2026)",{"type":7,"value":8,"toc":315},"minimark",[9,13,16,22,27,39,47,61,64,67,71,74,77,98,101,111,114,126,130,133,136,147,165,171,180,183,187,190,193,196,217,221,224,248,251,285,303],[10,11,12],"p",{},"Turn on Railway PR environments and every pull request gets a running copy of your app at a public URL. Railway's docs describe what that copy contains without much ceremony: a PR environment \"replicates your entire base environment, including services, networking, and variables.\"",[10,14,15],{},"That base is production by default.",[17,18,19],"tldr",{},[10,20,21],{},"Railway PR environments clone your base environment, variables included, and the default base is production. Sealed variables are the exception: Railway never copies them, so the safe thing breaks your preview and the unsafe thing works fine. The fix is one setting, and it is not per-variable overrides. Change the base environment PR previews inherit from, so they clone staging instead.",[23,24,26],"h2",{"id":25},"what-a-pr-environment-actually-contains","What a PR environment actually contains",[10,28,29,30,34,35,38],{},"When you enable the feature under ",[31,32,33],"strong",{},"Project Settings"," then ",[31,36,37],{},"Environments",", Railway starts watching for pull requests. Open one and it provisions a full copy of the base environment: your services, the private networking between them, and every variable those services can see. Merge or close the PR and Railway de-provisions the whole thing.",[10,40,41,42,46],{},"The convenience is real. Your branch deploy gets the same ",[43,44,45],"code",{},"DATABASE_URL",", the same API keys, and the same reference variables as production, with nothing to configure. That is also the problem.",[48,49,50,55,58],"warning-box",{},[51,52,54],"h4",{"id":53},"the-default-base-is-your-production-environment","The default base is your production environment",[10,56,57],{},"Railway's guide states it plainly: \"By default, this base is your production environment, but you can change which environment PR Environments inherit from in your Railway project settings.\"",[10,59,60],{},"If you have never opened that setting, every preview deploy in your project has been running with production credentials.",[10,62,63],{},"The preview is reachable too. Asked directly on Railway's community forum how domains work for previews, Railway employee Brody answered: \"As long as the service in the base environment has a service domain, the service in the PR environment will also have a domain, and we will show that in a table that our bot makes in a commit on the GitHub PR.\"",[10,65,66],{},"So the URL is public, and it is posted into the pull request where anyone with repo read access can see it. Nothing sits in front of it except whatever auth your app implements.",[23,68,70],{"id":69},"the-sealed-variable-trap","The sealed variable trap",[10,72,73],{},"Railway's answer to \"don't let people read this secret\" is sealing. A sealed variable is \"provided to builds and deployments but is never visible in the UI nor can it be retrieved via the API,\" and the CLI won't return it either.",[10,75,76],{},"Now read the restriction Railway lists on the same page:",[78,79,80,84,87],"danger-box",{},[51,81,83],{"id":82},"sealed-variables-are-not-copied-into-pr-environments","Sealed variables are not copied into PR environments",[10,85,86],{},"Two lines from Railway's variables documentation: \"Sealed variables are not copied over when creating PR environments\" and \"Sealed variables are not copied when duplicating an environment.\"",[10,88,89,90,93,94,97],{},"The variable isn't blanked or set to a placeholder. It's absent. Your service starts, reads ",[43,91,92],{},"process.env.STRIPE_SECRET_KEY",", gets ",[43,95,96],{},"undefined",", and crashes or silently misbehaves.",[10,99,100],{},"Put those two behaviours next to each other and you get a genuinely bad incentive:",[102,103,104,108],"ul",{},[105,106,107],"li",{},"Seal the secret, and preview deploys break with a missing-variable crash on every PR.",[105,109,110],{},"Leave it unsealed, and the production value is copied into a publicly-addressable environment every time anyone opens a pull request.",[10,112,113],{},"Whoever is on call for \"previews are broken again\" will reach for the second option. It fixes the build in about four seconds and nothing in the UI suggests a cost. Sealing also can't be undone, so the person who sealed it originally may not even be able to walk it back without rotating the key.",[115,116,117],"info-box",{},[10,118,119,120,125],{},"This is the same asymmetry we've written about in ",[121,122,124],"a",{"href":123},"/blog/how-to/railway-shared-variables","Railway shared variables",": the protection Railway offers is real, but it stops following the variable at exactly the boundaries where a copy of your environment gets created.",[23,127,129],{"id":128},"fix-it-at-the-base-not-per-variable","Fix it at the base, not per variable",[10,131,132],{},"The obvious instinct is to override individual variables inside each PR environment. That path is thinner than it looks. A developer on Railway's forum ran into it directly, reporting that \"PR environment overrides does not enable custom environment variable overrides to be set,\" and no Railway staff correction followed. You're not going to talk your way out of this one variable at a time.",[10,134,135],{},"Change what previews inherit from instead.",[137,138,140],"step",{"number":139},"1",[10,141,142,143,146],{},"Create a ",[43,144,145],{},"staging"," environment if you don't have one. Give it its own database, its own Stripe test keys, its own everything. This is the environment you're willing to have copied onto a public URL.",[137,148,150],{"number":149},"2",[10,151,152,153,34,155,157,158,161,162,164],{},"In ",[31,154,33],{},[31,156,37],{},", change the base environment for PR environments from ",[43,159,160],{},"production"," to ",[43,163,145],{},".",[137,166,168],{"number":167},"3",[10,169,170],{},"Open a throwaway pull request. Read the variables Railway provisioned into the resulting environment and confirm none of them are live credentials. Do this by eye once, because the whole failure mode here is assuming rather than looking.",[137,172,174],{"number":173},"4",[10,175,176,177,179],{},"Seal the production secrets in ",[43,178,160],{}," only. They were never going to reach a PR environment anyway, and now that's a property of your setup instead of an accident.",[10,181,182],{},"After that change, a preview running with a leaked staging key is an annoyance. Before it, it's an incident.",[23,184,186],{"id":185},"what-railway-already-does-right","What Railway already does right",[10,188,189],{},"One risk that sounds obvious turns out not to apply, and it's worth knowing so you don't spend a sprint defending against it.",[10,191,192],{},"Railway won't deploy a pull request branch from someone outside your workspace unless that person has been invited to the project with the same GitHub account that opened the PR. A drive-by PR from a stranger on a public repo does not provision an environment and does not get your variables. That is a deliberate safety property, and it means the exposure here is scoped to people who already have some level of access.",[10,194,195],{},"The remaining surface is the preview URL itself, which is public, indexed by nothing but shared in a place plenty of people can read.",[197,198,199],"tip-box",{},[10,200,201,202,205,206,209,210,213,214,216],{},"Railway sets ",[43,203,204],{},"RAILWAY_GIT_BRANCH",", ",[43,207,208],{},"RAILWAY_GIT_COMMIT_SHA",", and ",[43,211,212],{},"RAILWAY_GIT_AUTHOR"," in PR environments. Check for ",[43,215,204],{}," at boot and refuse to start if a production-only credential is also present. That turns \"someone left a live key in a preview\" from a thing you find out about later into a failed deploy.",[23,218,220],{"id":219},"a-quick-audit","A quick audit",[10,222,223],{},"Five minutes, and you'll know where you stand:",[225,226,227,232,236,240,244],"checklist-section",{},[228,229],"checklist-item",{"description":230,"label":231},"Project Settings, Environments. If it says production, that is the environment being copied onto public preview URLs.","Check the base environment",[228,233],{"description":234,"label":235},"Open any current PR environment and read its variables. Look for anything with live, prod, or sk_live in the name or value.","List what a live preview is holding",[228,237],{"description":238,"label":239},"Any secret readable in the production environment's UI is not sealed, which means it is being copied into every preview.","Find the un-sealed production secrets",[228,241],{"description":242,"label":243},"The preview URL is posted on the pull request. Everyone with read access to the repo has it, including contractors and former team members you have not removed.","Look at who can see the PR",[228,245],{"description":246,"label":247},"Closed PRs should have no environment left. Railway de-provisions on merge or close, but a project with manual environments alongside PR ones is easy to lose track of.","Confirm teardown",[10,249,250],{},"Once the preview is deployed, the variables are only half the story. Whatever the app exposes on that public URL is exposed for real: open endpoints, keys shipped into the client bundle, missing auth on an admin route. CheckYourVibe scans a deployed URL, and a preview URL works as well as a production one, which makes a PR environment a reasonable place to catch a problem before it ships.",[252,253,254,261,267,273,279],"faq-section",{},[255,256,258],"faq-item",{"question":257},"Do Railway PR environments inherit production variables?",[10,259,260],{},"Yes, by default. Railway's docs state that a PR environment replicates your entire base environment including services, networking, and variables, and that the base is your production environment unless you change it in Project Settings.",[255,262,264],{"question":263},"How do I enable PR environments in Railway?",[10,265,266],{},"Go to Project Settings, open the Environments tab, and click Enable PR Environments. Railway then spins up a temporary environment for each pull request and de-provisions all its services when the PR is merged or closed.",[255,268,270],{"question":269},"Why is my sealed variable missing in a Railway PR environment?",[10,271,272],{},"Railway does not copy sealed variables into PR environments, and the same restriction applies when you duplicate an environment. The variable name is absent rather than empty, so the service fails at startup instead of falling back to a default value.",[255,274,276],{"question":275},"Can someone open a pull request to get my Railway variables?",[10,277,278],{},"No. Railway will not deploy a PR branch from a user outside your workspace unless they have been invited to the project with the GitHub account that opened the pull request. An outside contributor's PR does not provision an environment.",[255,280,282],{"question":281},"Are Railway preview URLs public?",[10,283,284],{},"Yes. If the base environment's service has a domain, the PR environment's copy gets one too, and Railway's bot posts it in a table on the GitHub pull request. Nothing authenticates that URL unless your application does.",[286,287,288,294,299],"related-articles",{},[289,290],"related-card",{"description":291,"href":292,"title":293},"The full variables workflow, from the dashboard UI to the CLI and reference syntax.","/blog/how-to/railway-env-vars","Railway Environment Variables: Complete Setup Guide",[289,295],{"description":296,"href":297,"title":298},"How sealing works, why it cannot be undone, and the five ways a sealed secret still leaves your project.","/blog/how-to/railway-secrets","Railway Secrets and Sealed Variables",[289,300],{"description":301,"href":123,"title":302},"Why linking is manual, what a shared variable puts in every build, and the four places sealed variables do not follow.","Railway Shared Variables Explained",[304,305,308,312],"cta-box",{"href":306,"label":307},"/","Scan Your Preview URL",[23,309,311],{"id":310},"check-what-your-preview-is-exposing","Check What Your Preview Is Exposing",[10,313,314],{},"Paste a Railway preview URL into CheckYourVibe and see the exposed keys, missing auth, and open endpoints before the branch reaches production.",{"title":316,"searchDepth":317,"depth":317,"links":318},"",2,[319,320,321,322,323,324],{"id":25,"depth":317,"text":26},{"id":69,"depth":317,"text":70},{"id":128,"depth":317,"text":129},{"id":185,"depth":317,"text":186},{"id":219,"depth":317,"text":220},{"id":310,"depth":317,"text":311},"how-to","2026-08-25","Railway PR environments replicate your base environment, variables included, onto a public preview URL. Here is what gets copied, what silently does not, and the setting to change first.",false,"md",[331,333,335,337,339],{"question":257,"answer":332},"Yes, by default. Railway's docs say a PR environment replicates your entire base environment including services, networking, and variables, and that base is production unless you change it in Project Settings.",{"question":263,"answer":334},"Project Settings, then the Environments tab, then Enable PR Environments. From then on Railway spins up a temporary environment for each pull request and de-provisions it when the PR is merged or closed.",{"question":269,"answer":336},"Railway does not copy sealed variables into PR environments, and the same applies when duplicating an environment. The name is absent entirely, so the service crashes at startup instead of falling back to a default.",{"question":275,"answer":338},"No. Railway will not deploy a PR branch from a user outside your workspace unless they have been invited to the project with the GitHub account that opened the PR. An outside contributor's PR does not spin up an environment.",{"question":281,"answer":340},"Yes. If the service in the base environment has a domain, the PR environment's copy gets one too, and Railway's bot posts the URL in a table on the GitHub pull request. There is no auth in front of it unless your app provides it.","yellow",null,"railway pr environments, railway pr environment variables, railway preview deployments, railway pull request environment, railway ephemeral environment, railway base environment, railway sealed variables pr environment",{},true,"Every Railway PR environment clones your production variables onto a public preview URL. What gets copied, what does not, and the one setting that fixes it.","/blog/how-to/railway-pr-environments","8 min read","[object Object]","HowTo",{"title":5,"description":327},{"loc":347},"blog/how-to/railway-pr-environments",[],"summary_large_image","MggcPl8Apv2zkwSBGJV5DEDPtBb8eiBKyQNy1gyjJ2Q",1787862046434]