Skip to main content

Railway Locked Enterprise Deploys to a GitHub Org Allowlist: Build the Same Guardrail on Your Own Fleet

9 min readDora NodaDora Noda
Share
On this page

On May 22, 2026, Railway gave its chat agent a computer and locked the front door in the same changelog. Changelog #0291 shipped sandbox VMs that let the Railway Agent clone a repo, run commands, and open pull requests on its own — and, in the same release, GitHub org guardrails that let enterprise workspaces restrict deployments to an allowlist of approved GitHub organizations. One feature hands an AI agent the keys to the deploy path; the other decides which source repositories those keys can ever touch.

That pairing is the story. Not the checkbox — the admission that the deploy path now needs source policy as a primitive. When agents open PRs from anywhere, "which orgs may deploy here" stops being an onboarding-doc suggestion and becomes an enforcement point. Here is what Railway's guardrail actually does, why org-level is the right granularity, and how to build the same thing on a self-hosted Kubernetes fleet — with working YAML you can adapt today.

What the checkbox enforces — and its self-hosted shape in 30 seconds

The mechanics, straight from Railway's changelog: workspace admins toggle Restrict deployments to approved sources on, add one or more GitHub organizations to the allowlist, and Railway blocks any deployment whose source repository isn't owned by an approved org. The GitHub repo picker filters down to allowed owners too, so members can't accidentally pick a repo they shouldn't deploy from. The feature is available to workspaces on a spend commitment, and it follows the same pattern as Railway's earlier guardrails (Restrict Generate Domain, Restrict TCP Proxy): admins set the boundary once, everyone else operates inside it by default.

Be precise about what it does not do. It is not code review, not dependency scanning, not a secrets check. It answers exactly one question — "which orgs may deploy here?" — and answers it in the deploy path, where it can't be skipped. That narrowness is the point: repo-by-repo trust ("is this particular repo okay?") doesn't scale once humans and agents are creating repos and forks faster than anyone can review them. Org-level policy moves the decision up one level, to the unit you actually govern.

If you run your own fleet, here is the entire mapping up front — the rest of this post fills in each row:

Railway primitiveSelf-hosted equivalentWhere it enforces
Approved-org allowlistArgoCD AppProject.spec.sourceRepos globsGitOps sync: apps referencing other sources can't be created
Approved-org allowlistKyverno ClusterPolicy on Flux GitRepository/OCI sourcesAdmission: non-matching source objects rejected at apply time
Blocked deploy + filtered pickerCI/webhook owner check before buildDeploy path: pipeline refuses repos outside the allowlist

And the smallest version of the idea, an ArgoCD project that only syncs from two approved orgs:

yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-platform
  namespace: argocd
spec:
  description: "Deploys may only sync from approved GitHub orgs"
  sourceRepos:
    - https://github.com/my-org/*
    - https://github.com/my-org-platform/*
  destinations:
    - namespace: "team-*"
      server: https://kubernetes.default.svc

An org name becomes a URL glob; everything else is rejected when the Application is created, not after it ships. That is the whole trick. Now, why it's worth building.

Why org-level, and why now

Two trend lines crossed in the last year, and both point at the deploy source as the control point that matters.

First, the supply chain moved upstream of the firewall. Software supply chain attacks more than doubled globally during 2025, with 35% originating through compromised dependencies and 22% targeting CI/CD pipelines and build environments directly, per the year-end analysis summarized by CXOtoday. The shape of the damage changed too: the Shai-Hulud worm self-propagated through nearly 800 npm packages across three waves, and the 2026 LiteLLM compromise touched more than 2,500 organizations and over 430,000 CI/CD pipelines, SecurityWeek reported. When the blast radius of one trusted source is measured in hundreds of thousands of pipelines, "which sources are trusted" is no longer a procurement question — it's a runtime gate.

Second, agents industrialized PR creation. Stripe's internal agents now generate over 1,300 production pull requests per week; a large-scale Microsoft study found developers using command-line coding agents merged roughly 24% more PRs; and a March 2026 audit found that in 26 of 30 agent-authored PRs, the agent introduced at least one security issue. Small teams feel it most: repos with one to five contributors average more than 50 agentic PRs apiece. Every one of those PRs arrives from somewhere — a fork, a bot branch, a sandbox clone — and each somewhere is a deploy source your platform either governs or doesn't.

Put the two lines together and the conclusion is unavoidable: the unit of trust has to be the org, enforced mechanically in the deploy path. Humans can't review every repo into existence anymore, and agents won't wait for them to try.

The self-hosted equivalent, layer by layer

Railway's guardrail is one checkbox because Railway owns the whole deploy path. On your own fleet, the deploy path has three doors, and you need the allowlist on all three — each catches what the others miss.

Layer 1: GitOps source pinning (ArgoCD). The sourceRepos field on an AppProject is the closest thing to Railway's toggle that exists in the CNCF landscape: an allowlist of source URLs, with glob support, evaluated when an Application is created or updated. The snippet in the previous section is the whole implementation. What it blocks: anyone pointing an ArgoCD app at an unapproved org, fork, or personal repo. What it misses: everything that doesn't flow through ArgoCD — kubectl apply from a laptop, a second GitOps tool, a CI job that pushes manifests directly.

Layer 2: admission policy on sources (Flux + Kyverno). If Flux is your reconciler, the source objects themselves (GitRepository, OCIRepository) are Kubernetes resources, which means admission control can police them. This Kyverno policy rejects any Git source outside the approved orgs:

yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-git-sources
spec:
  # Start in Audit; flip to Enforce after the rollout (see below).
  validationFailureAction: Audit
  background: true
  rules:
    - name: allow-approved-orgs-only
      match:
        any:
          - resources:
              kinds:
                - GitRepository
      validate:
        message: "Git source must live in an approved GitHub org."
        anyPattern:
          - spec:
              url: "https://github.com/my-org/*"
          - spec:
              url: "git@github.com:my-org/*"

Note the two URL forms: developers clone over HTTPS, automation often uses SSH, and an allowlist that covers only one is a gate with a side door. Mirror the same policy for OCIRepository if you sync Helm charts or OCI artifacts. What this blocks: source objects created by any client, GitOps or human. What it misses: sources that never become cluster objects — a CI pipeline that builds from an arbitrary repo and pushes an image straight to the registry.

Layer 3: deploy-path owner check (CI/webhook). Close the remaining gap where builds start: before the pipeline builds anything, resolve the repo's owner and compare it against the allowlist. In practice this is a short step at the top of the pipeline (or a webhook receiver in front of it) that fails closed on unknown owners — the self-hosted version of Railway filtering the repo picker. It also covers the case neither GitOps layer sees: image-based deploys where the "source" is a registry path, not a git URL. The owner list should be the same data as layers 1 and 2, ideally rendered from one file so the three layers can't drift apart.

Fleet distribution: define once, inherit everywhere. On a Cluster-API fleet, none of this should be applied cluster by cluster. Define the AppProject, the Kyverno ClusterPolicy, and the allowlist data on the management cluster and push them to every workload cluster through the GitOps tool itself — an ArgoCD ApplicationSet matching all workload clusters, or the equivalent Flux Kustomization per cluster. A new cluster then inherits the guardrail at bootstrap, before its first tenant deploys anything. The org-to-URL translation lives in one place:

Org entryHTTPS patternSSH patternOCI/chart pattern
my-orghttps://github.com/my-org/*git@github.com:my-org/*ghcr.io/my-org/*
my-org-platformhttps://github.com/my-org-platform/*git@github.com:my-org-platform/*ghcr.io/my-org-platform/*

One allowlist file, three enforcement layers, every cluster. That is the honest self-hosted equivalent of the checkbox — more moving parts, but the same guarantee: nothing deploys from a source you didn't approve.

Rollout: three worked allow/deny cases

The policy is easy; the rollout is where fleets get bitten. Start every layer in audit mode — Kyverno validationFailureAction: Audit with PolicyReport review, ArgoCD dry-runs, a CI check that warns before it fails — then walk these three cases, which cover nearly every "but what about…" you'll hear:

CaseVerdictReason
Fork of an approved repo under a personal accountDeny by defaultThe fork's owner isn't approved; code lineage doesn't transfer trust. Allow explicitly per fork-owner if a workflow needs it.
Personal repo, later transferred into the approved orgDeny until transferOwner check runs at deploy time, so the repo becomes allowed the moment the transfer completes — no policy change needed.
Agent sandbox clone pushed back as a PR from a bot forkJudge by head ownerThe PR's base may be approved, but the code comes from the head. Require the head inside an approved org, or add an explicit bot-identity entry.

The agent case deserves emphasis because it's the one Railway's changelog is really about. An agent that clones a repo into a sandbox and opens a PR from a fork or a bot account is doing exactly what the Railway Agent's sandbox VMs do — legitimate work, arriving from an unapproved source. If your policy judges PRs by their base branch, agents walk through; judge by the head owner (and the bot identity behind it), and the guardrail holds without blocking agent workflows that push back into approved orgs.

Run in audit mode for at least one full sprint, review every would-be denial, encode the legitimate exceptions (template repos, sanctioned forks, bot identities) as explicit allowlist entries — then flip to enforce, one layer at a time, ArgoCD first and admission last. Exceptions as data, not as holes: anyone reading the allowlist file should be able to explain every entry.

Source policy is becoming table stakes

Railway shipping org guardrails in the same changelog as agent sandbox VMs is the tell: the platforms closest to agent-driven development already treat "which sources may deploy" as load-bearing infrastructure, not enterprise upsell decor. The supply-chain numbers explain the urgency; the agent-PR numbers explain the timing. Repo-by-repo trust was built for a world where humans created every deploy source by hand. That world is gone.

The good news for self-hosted fleets is that every primitive already exists — sourceRepos, admission policies, pipeline owner checks — and the hard part was never the YAML. It was deciding that the deploy path, not the onboarding doc, is where source trust gets enforced. Make that decision, define the allowlist once, push it to every cluster, and your fleet has the same guarantee as the checkbox.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide