Why AI-Built Apps Break · For engineers

Zeabur hosting breach: 3.2M env vars read in a hunt for AI keys

Admin tokens that never expired let an attacker into the hosting platform Zeabur, and a chain of over-broad keys led to 3.2 million stored variables. Days later Railway was hit for the same loot. What the reports confirm, what they don't, and what to change in your own setup.

Short answer In October 2026 attackers broke into Zeabur and Railway, two app-hosting platforms, and reached the secrets customers stored there as environment variables, including AI provider API keys. If you host apps: give each its own capped AI key, rotate every stored secret after a platform incident, and expire admin credentials.

Zeabur and Railway are hosting platforms of the kind called a PaaS: you connect your code, they build and run the app, and you paste its secrets, such as database passwords and AI provider API keys, into their settings as environment variables. In October 2026 attackers broke into both, five days apart, and reached those stored variables. Zeabur, judging from the attacker's commands, believes it was mostly after AI keys,Source 1 and Railway says the same of its own attacker.Source 2 If you keep an OpenAI or Anthropic key on any hosting platform, assume it is worth stealing on its own, and make it cheap to lose: one capped key per app, rotated after any platform incident.

At 01:50 UTC on 16 October, an OpenAI key kept in a Zeabur environment variable started sending image generation requests, about 1,107 of them before 02:56.Source 1 Zeabur thinks the key came from its breach two days earlier and says it cannot prove that. Most of the databases the attacker ran commands in belonged to n8n, a popular self-hosted tool that wires apps and AI models into automated workflows and stores the login for every service it connects to.

§1

A no-expiry admin token started a chain that ended at 3.2 million variables

Zeabur's preliminary report, dated 19 October, labels each finding as confirmed, assessed or hypothesis.Source 1 In UTC, using only what it confirms or assesses:

  1. 14 Oct, 01:36 and 01:40: the attacker starts using Zeabur's internal AWS keys, then the admin tokens for its shared cluster, the Kubernetes servers where many customers' apps run side by side. Both sat in plaintext in a private configuration repository.
  2. 14 Oct, 12:39: with the admin token, the attacker reads the settings and secrets of every service on the shared cluster, environment variables included, across 9 regions (confirmed where logs exist; Zeabur treats the rest as read).
  3. 14 Oct, 13:40 to 17:04: read commands inside customer database containers, first a few new-api/one-api gateways (open-source proxies that pool AI provider keys behind one endpoint), then n8n at scale, then PostgreSQL table by table.
  4. From 14 Oct, 13:40: root sign-ins to 101 customer servers on Google Cloud (GCP), using root passwords stored in plaintext in their initialization settings and readable with an old GCP service account key. SSH keys added on 50.Source 1
  5. 15 Oct, 14:52 to 16:51: using a key that an old cluster backup exposed, the attacker reads about 53 production secrets, including the internal network's admin credentials.
  6. 15 Oct, 15:30: from a rooted GCP server, the attacker uses those credentials to join Zeabur's internal network, whose rules let it reach the platform database. It reads the database from 16:10 to 16:47 with an internal read-only account.

From that database read, Zeabur confirms 3,218,965 environment-variable records, names and values, and 321,810 accounts with email, username and phone number.Source 1 No single failure did it. An admin token, a backup holding a key, a network rule and an over-broad read-only account each opened the next door.Source 1

Nobody was paged. Zeabur counts roughly 36 hours from the first credential use to the start of its investigation with no alert, because the attacker used valid credentials on legitimate paths; a customer report started the response.Source 1 Railway's timeline begins the other way: its latency monitoring paged on-call at 13:58, the first minute it records.Source 2

What the Zeabur attacker reached: 3,218,965 env-var records, 321,810 accounts, 865 database services (736 n8n), 602 of 607 n8n keys in env vars, root on 101 servers; compared with Railway's breach five days later.
Image description

Title: Zeabur, 14–15 October 2026: what the attacker reached. Subtitle: counts from Zeabur's preliminary report, then Railway's incident five days later. Six tiles. 3,218,965 environment-variable records read, names and values. 321,810 accounts read: email, username, phone. 865 database services with commands run; 736 were n8n. 602 of 607 n8n instances checked kept their encryption key in an env var (highlighted). 101 GCP servers with root sign-ins; SSH keys added on 50. AI keys: the main target, in Zeabur's reading; Amazon Bedrock calls with Zeabur's AWS keys all failed (highlighted). Table 'Five days apart, same loot', Zeabur versus Railway. When (UTC): Zeabur 14 Oct 01:36 to 15 Oct 21:05; Railway 20 Oct 13:58 to 22 Oct 03:32. Way in: Zeabur, plaintext admin tokens and AWS keys in a private config repo, the tokens never expired; Railway, privilege escalation in build infrastructure, then CI credentials. Exposed: Zeabur, env vars stored in the platform database and service secrets across 9 regions; Railway, env vars of at most 1.85% of workspaces, about 100,000. Motive (highlighted): Zeabur, AI service keys (Zeabur's observation); Railway, AI provider keys for distillation (Railway's assessment). Outcome: Zeabur winding down, shared clusters paused 1 Dec, all off by 31 Dec; Railway still running, builders moved to single-tenant. Sources: Zeabur preliminary report (19 Oct) and shutdown notice (17 Oct); Railway preliminary report (22 Oct, updated 26 Oct).

Fig. 1 — Zeabur's own counts, and how Railway's incident compares. Both companies say the attacker was after AI provider keys.
§2

The first line of BleepingComputer's report, the most-read news story on the breach, says the attacker stole over 3.2 million environment-variable records.Source 3 The number is right. What it counts is not keys.

An environment-variable record is any variable, secret or not. Zeabur confirms the queries and their row counts; its logs don't keep what came back, so it treats everything read as leaked. Inside the 865 database services, 736 of them n8n, only the commands were logged, and some failed.Source 1

The August link is weaker still. Zeabur files it under unconfirmed hypotheses and writes that it "should not be cited as fact".Source 1 What is confirmed: the credentials used in October were never rotated after August. Neither caveat changes what you do. Zeabur still tells every user to replace every key.

§3

Why a token from January 2024 still worked in October 2026

The cluster admin tokens were created in January 2024 with no expiry. The admin interface answered from the public internet. When Zeabur rotated keys after the August breach, these tokens were not on the list.Source 1

A key meant to read one storage bucket could read every bucket. The read-only database account could read all data. A deploy key for a service shut down four months earlier still worked. Internal and customer services shared a cluster, so reading customer settings also exposed Zeabur's own credentials.Source 1

Once a secret goes into a PaaS, its safety is set by the platform's worst-kept admin credential and by its CI, not by the padlock icon next to your variable.

§4

Railway, five days later: a build server, then CI, then decrypted variables

Railway's attack ran from 13:58 UTC on 20 October to 03:32 UTC on 22 October, according to its preliminary report.Source 2 The attacker escalated privileges in Railway's build infrastructure and later reached credentials for its CI environment, the system that builds customer code. From there it decrypted a subset of the encrypted environment variables.Source 2

Railway encrypted its variables and still lost some. Envelope encryption, where each value's key is itself encrypted by a master key, protects data from someone who copies the database. It does not protect it from someone holding the credentials the platform itself uses to decrypt.

On scale, Railway says "at most 1.85% of all workspaces" had variables potentially compromised, about 100,000 workspaces.Source 2 That is a ceiling, not a count, and the group Railway contacted as high risk is larger. Its status page adds that workspaces outside that group "are not confirmed safe from compromise."Source 4

On motive, Railway believes the attacker wanted AI provider keys for distillation: training a model on another model's answers.Source 2 That comes from Railway's own forensics; no one else has published evidence for it yet.

Railway asked vendors including GitHub, Anthropic, OpenAI and Stripe to revoke keys, and for some vendors it sent every key on the platform. A revocation email from your provider does not mean your key was in the decrypted set.Source 2

Railway calls its incident "part of a series of attacks recently targeting platform companies".Source 5 Neither company names an actor, and Zeabur's report does not mention Railway, so treat the link as open.

§5

602 of 607 n8n instances kept the key next to the data

Zeabur's report shows the shopping list: the attacker searched the platform database for keys, ran reads against n8n and the new-api/one-api gateways at scale, and tried Amazon Bedrock, Amazon's hosted AI model service, with Zeabur's own AWS keys. Every Bedrock call failed and nothing was charged.Source 1

n8n encrypts the credentials you save in it with one encryption key before they reach its database. If you don't set a key, n8n generates one on first launch and stores it in its ~/.n8n folder.Source 6 On a PaaS, where containers are rebuilt, the usual way to keep the key stable is the N8N_ENCRYPTION_KEY environment variable.

At Zeabur that handed the attacker both halves. It ran read commands against n8n's PostgreSQL databases from inside their containers, and it read every service's environment variables with the admin token. Of the 607 n8n instances Zeabur checked, 602 had the key in an environment variable, so Zeabur assesses their third-party credentials were "effectively leaked in plaintext".Source 1

On one platform there is no spot for the key that a holder of that platform's admin token cannot reach: a mounted file or a secret store on the same cluster is reachable the same way. What you can change is how much a leak is worth and how fast you can recover.

  • Keep only scoped, low-limit keys in n8n.
  • On n8n's Enterprise plan, external secrets keep credential values in a vault you run, such as AWS Secrets Manager or HashiCorp Vault, and n8n loads them when needed.Source 7 The vault login still lives on the platform, but you get an audit log and one place to revoke.
  • Move n8n and its key off a shared cluster you don't control.
  • Know the recovery cost now. Zeabur's instruction is to replace the n8n encryption key and recreate every third-party credential in n8n.Source 1
§6

Cap your AI keys, rotate in order, find what never expires

Make every AI key cheap to lose

Use one key per app and per environment, so one leak means one revocation. On Anthropic's console, a key belongs to the workspace it was created in, and a workspace can carry its own spend limit below the organization's.Source 8 Give each deployed app its own workspace and a limit you could lose. Keys in the Default workspace can't get their own limit, so move deployed apps out of it.Source 8

OpenAI separates the two. A project spend alert only sends an email; the docs say alerts "don't stop API requests or enforce a spending cap".Source 9 A project spend limit with Enforce a hard limit turned on does stop them: requests fail with a 429, though enforcement is not instantaneous.Source 10 Set the hard limit at what you could lose, the alert well below it, and a per-model rate limit near your real peak. If your app has fixed egress IPs, an OpenAI project IP allowlist makes a stolen key useless anywhere else.Source 11 It does not help against an attacker who calls from inside the same platform, as Zeabur's could.

Rotate everything after a platform incident, in the right order

Zeabur says revoke first, then create and redeploy.Source 1 Railway's status page lists it as create the new credential, revoke the old one, update your variables, redeploy.Source 4 If you can't take any downtime, create the new key, redeploy with it, then revoke the old one at once; the stolen key stays live only for that deploy. Two traps from Zeabur's report: changing an environment variable does not change the password inside your database, and anything you rotated after an earlier incident needs rotating again.Source 1

Railway's list adds the checks: remove public network access to databases, review your GitHub audit log and app access, and check registry audit logs such as Docker Hub.Source 2 For Zeabur's GitHub App window, 15 October 15:05 to 18:54 UTC, check each repository for deploy keys and webhooks you don't recognize, and its Activity view for pushes you didn't make.Source 1 Don't expect a record of clones: GitHub's audit log keeps Git events, where it records them at all, for seven days.Source 12 Then read your AI provider usage pages back to 14 October, or 1 September if you had a Zeabur GCP server.Source 1

Find the credentials on your own systems that never expire

On AWS, run aws iam generate-credential-report until it reports COMPLETE, then aws iam get-credential-report --query Content --output text | base64 --decode > creds.csv. Read access_key_1_last_rotated and access_key_1_last_used_date (and the _2_ pair) for every user. Any key older than a year is a candidate for Zeabur's failure, and the <root_account> row should show no active keys.

On Google Cloud, run gcloud iam service-accounts list, then for each account gcloud iam service-accounts keys list --iam-account=SA_EMAIL --managed-by=user to list the keys people created, with their expiry dates; an expiry of 9999-12-31 means the key never expires. Zeabur's GCP path began with an old one.Source 1 On Kubernetes, kubectl get secrets -A --field-selector type=kubernetes.io/service-account-token lists long-lived service account tokens by name. kubectl get clusterrolebindings -o wide shows who holds cluster-admin through RBAC, but not client certificates in system:masters or admins granted through cloud IAM. On a managed cluster, also check the cloud side: aws eks list-access-entries --cluster-name NAME on EKS, IAM roles on GKE.

Then make the three changes Zeabur's report points to: admin tokens that expire, admin interfaces that don't answer from the public internet, and an alert when someone reads secrets in bulk.Source 1 The inventory you just built is also your rotation list. Zeabur's August rotation covered the keys it knew were exposed and missed the tokens sitting in its own configuration repository.Source 1

§7

Zeabur closes on 31 December; Railway keeps running

Zeabur's shutdown notice says it has stopped new purchases, will stop renewals and pause all shared-cluster services on 1 December, and will have everything off by 31 December. Dedicated servers go dark on their own expiry date from 1 December, so check yours.Source 13 If you are still there, rotate first and migrate second: a new host does not un-leak a key. Railway moved its builders from multi-tenant to single-tenant environments, rolled all its keys twice, and has builds running again.Source 2

  1. One AI key per app and environment So you can revoke one without touching the others.
  2. Cap spend where the provider can Give each Anthropic workspace its own spend limit. On OpenAI, turn on a project hard spend limit; an alert alone only emails.
  3. Rotate after any platform incident Replace every key the platform held, revoke the old ones, and change passwords inside databases too.
  4. Treat n8n as a credential vault Keep only scoped keys in it, and know how you would replace its encryption key and every credential.
  5. List every credential that never expires Admin tokens, cloud keys, service account tokens. Give each an expiry or a rotation date.
  6. Don't read a revocation email as proof Railway sent some vendors every key it held. Check usage, then rotate anyway.

Sources

  1. Zeabur: October 2026 security incident (preliminary report). Retrieved . ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩
  2. Railway: Preliminary Report: Oct 21st Security Incident. Retrieved . ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩ ↩
  3. BleepingComputer: Zeabur shutting down after second security breach in two months. Retrieved . ↩
  4. Railway status: incident QH8L8B3E. Retrieved . ↩ ↩
  5. Railway: Security Disclosure: Wednesday October 21st. Retrieved . ↩
  6. n8n docs: Set a custom encryption key. Retrieved . ↩
  7. n8n docs: External secrets. Retrieved . ↩
  8. Anthropic support: Creating and managing Workspaces. Retrieved . ↩ ↩
  9. OpenAI docs: Rate limits and spend. Retrieved . ↩
  10. OpenAI docs: Spend limits. Retrieved . ↩
  11. OpenAI docs: IP allowlist. Retrieved . ↩
  12. GitHub docs: Accessing the audit log for your enterprise. Retrieved . ↩
  13. Zeabur: Zeabur is shutting down: what happens next. Retrieved . ↩

Published · Updated Report an error (support@agentum.cloud)