Postgres seed data generator: fill your database without writing INSERTs
Seeding a PostgreSQL database means populating an empty schema with initial rows. The tool you choose decides how much time you spend on foreign keys, maintenance, and broken fixtures. This guide compares the options and shows the schema-driven approach that removes the work entirely.
A Postgres seed data generator is a tool that populates an empty database with initial rows. The options range from hand-written SQL scripts to ORM seeders to schema-driven generators that read your tables and foreign keys and produce referentially intact rows automatically. This guide compares the approaches, says honestly when each one fits, and shows the schema-driven workflow that removes the work entirely.
What is database seeding?
Seeding is the step after migrations: the schema exists, and you need rows in it — for local development, staging, demos, or CI. The rows have to respect the schema's rules: foreign keys must point at real parents, NOT NULL columns must be filled, and CHECK constraints must pass.
The difficulty of seeding scales with schema complexity. A three-table demo is trivial to seed by hand. A schema with composite primary keys, multi-level foreign key chains (users → orders → order_items), enums, and CHECK constraints is where hand-written seeds fall apart.
Four ways to seed a PostgreSQL database
| Approach | FK handling | Setup cost | Maintenance | Best for |
|---|---|---|---|---|
Hand-written SQL (INSERT, generate_series()) | Manual | Hours per table | Breaks on schema change | Throwaway demos |
ORM seeders (prisma db seed, rake db:seed, factory_boy) | Manual | Per-model code | Drifts from the schema | Unit-test fixtures |
Dump and restore (pg_dump) | Native | Trivial | PII risk, stale, heavy | Literal prod copies |
| Schema-driven generator (Weavori) | By construction | One command | Zero — follows the schema | Anything with foreign keys |
Honest guidance:
- Hand-written SQL is fine while your schema fits in one file. The day a foreign key chain appears, you start writing inserts in dependency order — and re-verifying them after every migration.
- ORM seeders are convenient because they live in your codebase, but they duplicate schema knowledge. When the schema changes and the seeder doesn't, your fixtures silently drift. Faker and factory_boy are excellent for single values; they do not resolve relationships for you.
- Dump and restore is the most faithful — exactly your data, including the PII. That's also why it's the wrong tool for most dev, staging, and CI environments.
- Schema-driven generation reads the schema as the source of truth, so seeding can't drift from it. This is the approach Weavori takes.
The deciding question: who should own the knowledge of how rows relate — your seed script, or the schema? The schema is always right. Seeders that re-encode it are always one migration behind.
Why foreign keys break seed scripts
Foreign keys are the hardest part of seeding, for a mechanical reason: children must reference parent rows, and parent IDs are not known until the parents are inserted.
Hand-written scripts handle this three ways, all fragile:
- Hard-coded IDs — breaks the moment an insert order changes or an ID sequence differs
- Query the parent after inserting — works, but doubles the script length and the drift surface
- Skip the constraint — guarantees you will hear about it in a test failure, later
Schema-driven generators handle it by construction: they read the foreign keys, compute the dependency order (parents before children), and stream a cache of generated parent IDs. Every row references a real parent — no manual ordering, no lookups, no hard-coded IDs.
How Weavori seeds a PostgreSQL database
Three steps, none of them writing SQL:
- Connect — point Weavori at the database, or paste
CREATE TABLEstatements (DDL paste mode) when the schema isn't deployed yet - Plan — Weavori introspects tables, foreign keys, types, and constraints, then shows a preview: estimated rows per table, generator choices, fallbacks
- Generate — parents before children, via PostgreSQL COPY, with constant-memory streaming
Row counts are per-table configurable. Custom CSV datasets, computed-column formulas, and table exclusions all work without touching SQL.
Seeding speed is not a trade-off
Typical schemas (under 200 tables) seed in under 2 minutes. The internal benchmark: 100,000 rows across 14 tables in 12.4 seconds, 25,000 rows in 2.1 seconds (pgbench-derived schema, Dockerized PostgreSQL 17, generation via COPY, July 2026).
Seeding in CI
Seeding belongs in the pipeline, not in a developer's local shell history. In CI the requirements change: headless authentication, parseable output, deterministic exit codes, and no phone-home licensing.
Weavori supports API keys for headless auth, plain and json output, standardized exit codes, and a fingerprint-based schema cache so repeat seeds skip introspection. Licenses validate offline — CI runners on restricted networks don't phone home. See the CI/CD guide for GitHub Actions, GitLab, CircleCI, and Jenkins examples.
Seeding vs. syncing
Seeding generates fresh rows for an empty database. Syncing copies real rows between databases in FK-safe order — Weavori's Pro sync command. If you need an exact copy of existing data (a staging mirror, a demo of real records), sync is the tool. If you need new data that behaves like production, seed.
Related guides
- Postgres test data — the five ways to get test data, compared
- Postgres synthetic data — synthetic vs. mock vs. anonymized
- How to generate realistic test data for PostgreSQL — the five practices, in depth
- Quickstart — first seed in minutes
- Comparison hub — Weavori against other tools, honestly