Skip to main content

Railway's Access Groups Are Group RBAC Behind a SaaS UI: Here's the Same Boundary as Kubernetes YAML

10 min readDora NodaDora Noda
Share

Railway's August 14, 2026 changelog entry looks small: Access Groups, better template editing, and Railway Agent Connectors. Under the hood it is a permissions primitive — the same group-scoped RBAC boundary platform teams have been drawing in Kubernetes for a decade, only this time rented behind a SaaS UI instead of owned as YAML.

If you run apps on Railway, the feature decides who can see which project. If you run your own fleet under Cluster API, the same decision lives one layer lower: in rbac.authorization.k8s.io.

Here is the mapping, up front, because this is the post:

Railway (rented)Kubernetes (owned)What it controls
Access Group (Workspace → People → Groups)Group subject (kind: Group) via OIDC claimWho belongs together
Project Role assigned to the groupRole / ClusterRole (rules: [...])What the group can do
Projects attached to the groupRoleBindings in each namespaceWhere the group can do it
Enforcement toggle (Workspace → People → Security, with preview of lost access)RBAC deny by default + admission / OPA / KyvernoHow the boundary is enforced
Workspace admin (sees every project regardless)ClusterRoleBinding → cluster-adminBreak-glass / platform owner
Direct project grant or creatorUser / ServiceAccount subject on a RoleBindingExceptions outside the group

The rest of this post puts real semantics behind each row — what Railway actually shipped, what the equivalent looks like as YAML you commit, and where owning the control plane stops being a skin change and starts being a different boundary.


What Railway actually shipped (August 14, with context)

Access Groups entered beta on August 14, 2026, for Enterprise workspaces on committed-spend plans. The mental model, from Railway's changelog:

  1. Admins group people, assign a project role, and attach that group to any set of projects at Workspace settings → People → Groups.
  2. Audit effective access at Project settings → Members.
  3. Groups can be configured before enforcement; enforcement itself is flipped at Workspace settings → People → Security. Railway shows a preview of who would lose access and which projects would be left without a non-admin member before you commit.
  4. Once enforced, non-admins only see a project if they have access via a group, a direct grant, or because they created it. Workspace admins retain access to every project regardless.

Two details matter for the comparison. First, the pre-enforcement preview exists because PaaS RBAC is additive and the blast radius of a misclick is a team losing deploy access — Railway has to simulate the deny before it applies it. Second, the creator bypass (you always see what you created) has no direct Kubernetes analog; Kubernetes has no object-ownership fallback, only bindings.

Railway Agent Connectors, also beta and behind a Feature Flag, sit on a different axis. Enabled at Workspace settings → Agents, they let an agent chat attach Notion (search pages and databases), Linear (issues, projects, cycles), Sentry (issues and stack traces), or a custom remote MCP server via OAuth. Each connector is owned by its creator, usable only in that person's agent runs, with private credentials, and attached per-chat through the Contexts picker. If Access Groups is about who can touch which project, Agent Connectors is about whose third-party context an agent can borrow while working inside that project — group RBAC and credential scoping, two halves of the same multi-tenant story.

The surrounding changelog makes the convergence clearer. Cloud Agents Beta landed August 7. DNS logs and log improvements landed in July. Railway is layering, in sequence, the three things a self-hosted control plane exposes on day one: compute agents you schedule, name-resolution visibility you can tcpdump, and a permission model you can audit as objects. On a rented platform each arrives as a vendor-prioritized feature with a changelog date; on a cluster you own they are API objects you already have.

The self-hosted translation: the same boundary as YAML

On a Cluster API fleet the unit of tenancy is typically a namespace the way a Railway project is a unit of tenancy. The PaaS translates both to the same question: which groups can act in which projects, with which verbs?

Start with the role — the what:

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: project-deployer
rules:
  - apiGroups: ["", "apps", "batch"]
    resources: ["pods", "deployments", "jobs", "services", "configmaps"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get", "list"]  # read-only; no create/delete — narrower than Railway's project role

Define it once, reuse it everywhere — this is the equivalent of Railway's project role.

Then bind it per namespace to a Group subject. This is the equivalent of attaching an Access Group to a set of projects:

yaml
# team-a can deploy in team-a, not in team-b
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-a-deploys
  namespace: team-a
subjects:
  - kind: Group
    name: team-a-devs          # from OIDC id_token groups claim
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: project-deployer
  apiGroup: rbac.authorization.k8s.io
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-a-deploys
  namespace: shared-staging
subjects:
  - kind: Group
    name: team-a-devs
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: project-deployer
  apiGroup: rbac.authorization.k8s.io

Repeat for team-b-devsnamespace: team-b. No ClusterRoleBinding — that would be the equivalent of attaching the group to every project, which is the workspace admin row in the table.

Groups arrive from your identity provider. Wire the API server to your IdP via OIDC (--oidc-issuer-url, --oidc-client-id, --oidc-groups-claim, --oidc-groups-prefix) or a webhook authenticator, and the groups claim in the token becomes the Group subject RBAC evaluates. Railway's Groups UI is a database table you click; Kubernetes groups are a token claim you issue.

Two behaviors worth calling out because they differ:

  • Default deny. Kubernetes RBAC is deny by default — no binding, no access. Railway's enforcement toggle with preview exists precisely because their prior model was allow-by-default and flipping to deny needs a migration path. On your own cluster there is no toggle; the boundary exists the moment you stop granting ClusterRoleBinding and start granting RoleBinding.
  • Creator bypass. Railway's "you always see what you created" is a product affordance that prevents self-lockout on new projects. In Kubernetes, creating a namespace does not auto-bind you to it — you add that explicitly (or via an aggregated ClusterRole plus a controller that writes the binding). Stricter, more surprising to newcomers, easier to audit.

Where owning the control plane stops being the same boundary

If the previous section is "same shape, different owner," this section is where ownership actually changes what you can enforce.

1. The network boundary, not just the API boundary. Railway's Access Group controls who can see and mutate a project in the dashboard and API. It does not control whether team-a's workload can resolve or reach team-b's Service DNS name at runtime — that is the platform's private network. On your own cluster you enforce that with Kubernetes primitives the PaaS cannot expose per tenant:

  • NetworkPolicy default-deny per namespace, then explicit allow — the runtime companion to RBAC's API deny. Railway has private networking; you have a firewall object you commit.
  • Gateway API policy (HTTPRoute, ReferenceGrant, BackendTLSPolicy) scoped per namespace, so team-a cannot attach a route to team-b's gateway without a ReferenceGrant the platform team grants. This is group RBAC projected into L7 routing — a dimension a hosted PaaS collapses into "environments" rather than policy objects.
  • OPA Gatekeeper / Kyverno guardrails: "no RoleBinding may reference cluster-admin," "every namespace must have a NetworkPolicy," "no LoadBalancer without an allow-list annotation." Railway enforces its own invariants behind the API; you enforce yours as admission policy you can read and change.

2. Audit you keep. Railway offers Members inspection per project and presumably logs access changes somewhere you can request. On your own cluster, kube-apiserver audit logs are yours — you ship them to Loki or S3, you set the audit policy, you query which Group listed which Secret at 02:14 UTC. For regulated teams this is not a nice-to-have; it is the artifact an auditor asks for, and "the vendor has logs" is not the same answer as "here are the logs."

3. Custom aggregations. Railway's project roles are the roles Railway offers. A ClusterRole with aggregationRule lets you compose roles like packages:

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: platform-view
aggregationRule:
  clusterRoleSelectors:
    - matchLabels:
        rbac.platform/aggregate-view: "true"
rules: []  # populated by aggregation

Label a new ClusterRole with rbac.platform/aggregate-view: "true" and every platform-view holder gains its verbs automatically. This is how a self-hosted PaaS evolves permissions without a changelog entry — new capability, new label, no migration preview to click through.

4. Agent scope as K8s identity. Railway's Agent Connectors are per-creator, per-run, private-credential — correctly scoped to the human who linked Notion or Linear. On your own fleet the agent is a ServiceAccount with its own RoleBinding and, with projected tokens or SPIFFE, a short-lived identity whose access to Notion or Sentry is itself a ClusterRole you can audit. Same intent — don't let one tenant's agent read another tenant's Sentry — different enforcement layer.

The cost of owning it (honest tradeoffs)

Owning the boundary is not free. Railway's Access Groups preview exists so a team cannot lock itself out; on your own cluster you can — a bad RoleBinding edit removes deploy access with no preview toggle to save you. Three costs you accept when you own the control plane:

  • You run the API server's dependencies. etcd backups, API server upgrades, OIDC provider rotation, and the break-glass ClusterRoleBinding for the on-call account that can undo a bad RBAC change at 3 a.m. Managed Kubernetes (or a PaaS like Bex.co's fleet) absorbs some of this, but the RBAC objects remain yours to get right.
  • You operate the policy engine. Kyverno or Gatekeeper needs its own upgrade and test cycle; a badly written policy denies more than it protects. Railway tests its enforcement once for all tenants — you test yours per fleet.
  • You answer the "who left this project with no owner?" question yourself. Railway surfaces "projects left without a non-admin member" before you enforce. On your own cluster that check is a kubectl query or a policy you write — powerful, but only if you write it.

For many teams the rented model is the right call: Access Groups is a solid, shippable RBAC primitive, and Agent Connectors correctly scope third-party context per human rather than per workspace. The point of this post is not that Railway got the model wrong — they got it close to how the underlying system already works.

The point is where the boundary lives when it matters most. When audit asks for logs you keep, when network isolation must be a NetworkPolicy object not a dashboard toggle, when a team's agent must carry a ServiceAccount identity you rotate per run — that boundary lives in the control plane. Renting it moves the enforcement date to the vendor's changelog. Owning it moves the enforcement date to your next commit.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, and keep the RBAC, Gateway API policy, and audit logs that prove who could do what, where. The control plane is yours; the PaaS just makes it usable. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex