kdabrow/seeder-once
Laravel package that runs database seeders only once. It records executed seeders in a dedicated table, so repeat deployments or db:seed calls skip already-applied seeders—ideal for safely seeding production data without duplicating rows.
Feature: safe, repeatable reference-data seeding. Laravel's db:seed is re-runnable by design; anything that must exist exactly once (roles, permissions, plans, tax rates, countries/currencies, feature flags, system config, default tenant templates) currently relies on every developer writing idempotent firstOrCreate logic in every seeder. This package converts that from a code-discipline problem into a tracked, one-shot execution problem — a decision about where correctness lives (framework-level tracking vs. per-seeder defensive code).
Roadmap / delivery: unblocks automated deploy pipelines. Once seeding is idempotent-by-default, "run seeders on deploy" becomes a safe step in CI/CD (Forge, Vapor, Envoyer, GitHub Actions, Kubernetes jobs) rather than a manual, error-prone post-deploy ritual. That is typically a prerequisite for further work: environment parity, zero-touch provisioning, multi-tenant onboarding, and module/plugin installation flows that each need their own bootstrap data.
Build vs. buy: this is a "buy" — and it's free (MIT). The alternatives are (a) hand-rolling a seeder_log table plus a custom Artisan command in every codebase, (b) making every seeder idempotent forever, or (c) using migrations for seed data (an anti-pattern that couples schema history to content). Option (a) is roughly 1–2 days of build plus permanent maintenance; option (b) is an unbounded tax on every future seeder; option (c) is a known trap. For a 20-star package, the honest framing is buy the idea cheaply, own the risk — wrap it behind our own command so the dependency is one file deep and replaceable in an afternoon.
Use cases worth scoping explicitly: separating required seeders (production reference data, run once, tracked) from sample seeders (local/demo data, run on demand, never tracked). This package is the enforcement mechanism for the former only — a distinction worth codifying in the repo structure (database/seeders/Required vs database/seeders/Demo) at the same time.
Good fit when:
Look elsewhere or proceed with guardrails when:
migrate:fresh --seed on a developer machine silently skips seeders that are already marked as run. This is the single most likely source of confusion and should be verified before adoption.Diligence checklist before saying yes: the tracking store (table vs. cache — a cache-backed store would be a red flag in production), reset/undo command, behaviour under --class= targeted seeding, multi-tenant/multi-database support, test-suite presence, and the last-12-months commit cadence.
For executives — the one-liner: a small, free, MIT-licensed component that lets our deploy pipeline seed required system data safely, so it runs exactly once no matter how many times we deploy. It removes a class of production incidents (duplicate plans/roles/tax rows, or missing lookup data after a deploy) and removes custom tracking code we would otherwise maintain in every Laravel service.
Business framing: low cost, low blast radius, reversible. It sits behind our own Artisan wrapper, so if it is ever abandoned we replace one file — an afternoon, not a project. The measurable win is deleting a recurring failure mode and a recurring "did anyone run the seeder?" question from every release.
For engineering — the honest version: it replaces hand-rolled "has this seeder already run?" logic with a convention and a tracking record, which means seeders stop needing to be defensively idempotent and deploys get a deterministic seeding step. Keep it boring: pin the version, wrap the command, split Required from Demo seeders, document the local reset flow, and add one integration test that seeds twice and asserts row counts. We are adopting the pattern as much as the package; the package just means we don't write it.
How can I help you explore Laravel packages today?