Safeguard
Developer Tools•

How do you write a staging seed script that uses only fake data?

Write a seed script that builds staging data from a fixed random seed and reserved example domains, so no real person ever lands in your test database.

Safeguard Security Team
4 min read

A safe staging seed script generates every record from code: a fixed random seed, fake names, reserved example email domains, and no read access to production at all. Run it on an empty database, commit it to the repository, and it gives every developer the same dataset on demand.

What a seed script is

A seed script is a program that fills a database with known starting data. It differs from a production copy in one important way: nothing in it ever belonged to a real customer. That removes the need to mask, approve or later delete anything, because there is no personal data to protect.

The usual alternative is a restored dump of production with some columns scrubbed. That approach keeps real people in your test systems, and one missed column is a disclosure. A generator cannot leak what it never held.

Steps to build one

  1. Pick a data generator library for your language. Most have a seeding function that makes output repeatable.
  2. Set one fixed seed at the top of the script, and read it from an environment variable so you can change it on purpose.
  3. Create records in dependency order: accounts, then users, then orders, then invoices. Keep identifiers in memory so child rows can point at parents.
  4. Use reserved domains for email addresses (see the related post on safe email domains).
  5. Add a handful of hand written edge cases: an account with no users, a user with a very long name, a deleted record. Generators rarely produce these by themselves.
  6. Make the script refuse to run unless it is pointed at a database whose name matches a staging pattern. A guard like this stops the day someone runs it against the wrong host.

A short sketch of the shape, in TypeScript with a generator library:

import { faker } from "@faker-js/faker";

faker.seed(Number(process.env.SEED ?? 1234));

if (!process.env.DATABASE_URL?.includes("staging")) {
  throw new Error("Refusing to seed a non staging database");
}

const users = Array.from({ length: 200 }, () => ({
  name: faker.person.fullName(),
  email: faker.internet.exampleEmail(),
}));

Reproducibility has limits

The Faker documentation says that setting a seed gives consistent results, and that setting the seed again resets the sequence. It also warns that after upgrading to a new version you may get different values for the same seed, because the underlying lists of names and words can change. It adds that methods using relative dates are not made reproducible by a seed alone.

So pin the library version in your lockfile, and pass a fixed reference date to anything that depends on "now". If your tests compare exact values, those two habits prevent a surprise failure after a routine dependency update.

Mistakes to avoid

  • Generating values that look real and might be real. Random names are fine. Random phone numbers and street addresses can match actual people.
  • Seeding from a "sample" export of production "just to get the shape right". That brings real rows back in.
  • Letting the seed script live outside version control, so each team member has a private variant with different data.
  • Skipping the guard against non staging hosts.

Keep the seed script itself healthy

A seed script is code with dependencies, and it runs with database credentials. Review it like any other change, and include it in the same dependency scanning you run on the application. Safeguard can build an SBOM and scan the dependencies of a repository, including a tooling folder like this one, and you can try a free scan at gold.safeguard.sh/scan.

Try it yourself

Click through it at your own pace, or open it full screen. Click through the inventory and notice that tooling packages appear next to application ones.

Quick check

Is a fixed seed a security risk?

Not for fake test data. The values are meant to be predictable. Never use the same approach to generate real passwords, tokens or keys, even in staging.

How many records should the seed create?

Enough to exercise pagination and search, often a few hundred to a few thousand. For volume testing, add a separate larger profile rather than slowing every developer's local setup.

Can the seed script read production to copy the shape?

It should not. Describe the shape in code from the schema. Reading production, even only for column statistics, widens who and what touches real data.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.