Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Laravel Seeders vs Factories When to Use

By Kokil Thapa | Last reviewed: September 2026

Choosing between Laravel seeders vs factories when to use each tool is one of those small architectural decisions that compounds across every environment you maintain. Seeders insert deliberate, often fixed records—roles, tax rates, demo categories—while factories generate plausible fake data on demand for tests and local development. On production Laravel applications I have shipped since 2010, the teams that confuse the two end up with brittle tests, bloated staging databases, and seed scripts that break every time a column changes. This guide walks through the decision with Laravel 13.x patterns, real file paths, and the combination that keeps database migrations and seeding predictable from local through CI to production.

What is the difference between Laravel seeders and factories?

Both tools populate your database, but they answer different questions. A seeder is an orchestration class—typically under database/seeders/—that runs explicit inserts, often via Eloquent models or DB::table(), to put known rows into tables. A factory is a definition class under database/factories/ that describes how to build one model instance with randomized but realistic attributes using Faker.

In Laravel 13 (PHP 8.3 minimum), the wiring is standard. You register seeders in DatabaseSeeder and invoke factories from tests with User::factory()->create() or from seeders with User::factory()->count(50)->create(). The framework ships both features in the core testing and seeding stack documented at laravel.com/docs/13.x/seeding and laravel.com/docs/13.x/eloquent-factories.

Seeders vs Factories — Core DifferenceDatabase SeedersFixed, intentional rowsRuns via artisan db:seedSame output every timeModel FactoriesRandom plausible rowsRuns in tests and seedersDifferent each runboth write DBShared foundation: Eloquent models + migrationsMigrations define schema; seeders and factories fill itNever replace validation or business rules in either layer
Laravel seeders vs factories: seeders insert known records; factories generate variable test and dev data through the same Eloquent layer.

Seeders in one sentence

Seeders are scripts you run deliberately—php artisan db:seed or as part of deployment—to populate baseline data. They belong in version control because the data they insert is part of your application contract: default admin role, payment status enums, Nepal-specific lookup values if your app uses them.

Factories in one sentence

Factories are blueprints for creating model instances. They shine in PHPUnit or Pest tests where you need an Order with a related User but do not care whether the email is jane@example.com or bob@example.com. For deeper factory patterns, see the companion guide on Laravel model factories for realistic test data.

CriteriaSeedersFactories
Primary purposeLoad fixed reference or demo datasetsGenerate fake records on demand
Typical triggerphp artisan db:seed, deploy hook, CI setupModel::factory()->create() in tests
Data repeatabilityIdentical on every run (when written correctly)Varies per run unless you pin states
Production useYes—for lookup tables, initial config rowsNo direct production writes; definitions only
Coupling to FakerOptionalBuilt-in via fake() helper
Best paired withfirstOrCreate, idempotent checksFactory states, relationships, afterCreating

When should you use Laravel seeders instead of factories?

Reach for a seeder whenever the row itself matters—not just that a row exists. If removing or randomizing the data would break a feature, it belongs in a seeder, not a factory call scattered across tests.

Concrete cases I use seeders for on client projects and legal-tech portals:

  • Reference data — user roles, permission names, document types, court fee categories, order statuses. These rows have stable IDs or slugs your code references.
  • Environment bootstrap — a default admin account on staging, demo law-firm categories on a QA server, currency codes for an eCommerce catalog.
  • Idempotent production inserts — VAT rate rows, shipping zones, feature flags stored in the database rather than config files.
  • Cross-table baseline sets — when five tables must be populated together in a fixed relationship before the app boots cleanly.

Example: idempotent role seeder (Laravel 13)

// database/seeders/RoleSeeder.php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;

class RoleSeeder extends Seeder
{
    public function run(): void
    {
        foreach (['admin', 'staff', 'client'] as $role) {
            Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']);
        }
    }
}

Notice firstOrCreate. Production seeders must be safe to re-run after deploy. Blind insert() calls fail on the second run when unique constraints fire—a pattern I have debugged on more than one client portal with role-based access.

When seeders beat factories

  1. The application code reads specific slug or ID values.
  2. QA needs a predictable demo login every sprint.
  3. Staging must mirror production structure without 10,000 fake users.
  4. Compliance or audit requires known sample records—not random PII.

For enterprise apps with strict data boundaries, pairing seeders with enterprise application development practices keeps staging closer to production shape without importing real customer data.

When to Use Seeders vs FactoriesNeed database rows?Fixed values required?roles, settings, lookupsVolume + random OK?tests, local dev bulkUse SeederfirstOrCreate, explicit arraysUse Factorycreate(), states, relationsProduction: seeders yes — factory bulk create usually no
Decision tree for Laravel seeders vs factories when to use each: fixed contract data flows to seeders; variable volume flows to factories.

When should you use Laravel factories instead of seeders?

Factories win when you need shape without caring about content. Automated tests are the clearest case: each test should create only the models it needs, in isolation, without depending on a global seed state that another test might mutate.

Use factories when:

  • Writing feature or unit tests that need users, orders, bookings, or documents.
  • Prototyping UI locally with 200 products—not 200 hand-written arrays.
  • Stress-testing queries with large datasets in a disposable database.
  • Defining reusable "states" like ->unverified() or ->withMedia() that tests opt into.

Example: factory with relationships

// database/factories/BookingFactory.php
public function definition(): array
{
    return [
        'user_id' => User::factory(),
        'starts_at' => fake()->dateTimeBetween('+1 day', '+2 weeks'),
        'status' => 'pending',
    ];
}

public function confirmed(): static
{
    return $this->state(fn () => ['status' => 'confirmed']);
}

In a Pest or PHPUnit test:

it('allows confirmed bookings to be cancelled', function () {
    $booking = Booking::factory()->confirmed()->create();

    $this->actingAs($booking->user)
        ->delete(route('bookings.destroy', $booking))
        ->assertRedirect();
});

This test does not call db:seed. It owns its data. That isolation is why factories belong in your testing and optimization workflow, and why I avoid seeding entire databases before every test suite—a common mistake that slows CI from minutes to tens of minutes on larger apps.

Factory features worth learning in 2026

Laravel 13 factories support afterCreating callbacks for attaching media, creating pivot rows, or firing domain events. On a production booking app like Adventure Third Pole Trek, factories generated supplier links and itinerary lines in tests without duplicating that logic in seeders. Relationship shortcuts—User::factory()->hasPosts(3)—keep test setup readable.

External reference: the PHPUnit data provider docs at docs.phpunit.de complement factory usage when you parameterize edge cases across many synthetic records.

How do you combine Laravel seeders and factories in production apps?

The mature pattern is not seeders versus factories—it is seeders calling factories where bulk volume helps, plus factories standing alone in tests. Your DatabaseSeeder orchestrates specialized seeders; only some of them delegate to factories.

database/
├── factories/
│   ├── UserFactory.php
│   └── OrderFactory.php
├── seeders/
│   ├── DatabaseSeeder.php
│   ├── RoleSeeder.php          // fixed — no factory
│   ├── SettingsSeeder.php      // fixed — no factory
│   └── DemoCatalogSeeder.php   // calls Product::factory()->count(100)
└── migrations/

DatabaseSeeder orchestration

// database/seeders/DatabaseSeeder.php
public function run(): void
{
    $this->call([
        RoleSeeder::class,
        SettingsSeeder::class,
    ]);

    if (app()->environment('local', 'staging')) {
        $this->call(DemoCatalogSeeder::class);
    }
}

Production runs only the fixed seeders. Local and staging get demo volume via factories inside a guarded seeder. Never ship User::factory()->count(500)->create() unguarded in DatabaseSeeder—I have seen that inflate MySQL storage on a shared staging server and mask real performance issues.

Combined Seeding PipelinemigrateFixed SeedersEnv checkDemo seedFactory definitions (shared)Used by DemoCatalogSeeder locally AND by PHPUnit/Pest in CIProductionfixed seeders onlyStagingfixed + demo volumeCI testsfactories per test
Production Laravel apps combine migrations, fixed seeders, optional demo seeders, and per-test factories across environments.

Real-world split on an eCommerce Laravel app

On a platform like Nepal Gift Card, seeders hold payment method enums, gift-card denomination rows, and admin roles—data the checkout code expects. Factories generate thousands of synthetic gift-card orders in tests to verify idempotent redemption and queue jobs. The same OrderFactory never runs in production seeding; only the enum seeder does.

Align this with modern Laravel architecture: keep domain rules in services and actions, use seeders for bootstrap data, and use factories to exercise those rules under varied inputs. If you store JSON config blobs during development, validate structure with the JSON formatter before committing seeder payloads.

How do Laravel seeders and factories fit into CI/CD and testing?

Your pipeline should treat migrations, seeders, and factories as three separate concerns with different entry points.

CI/CD commands that actually work

# .gitlab-ci.yml excerpt — test job
script:
  - cp .env.testing .env
  - php artisan migrate --force
  - php artisan db:seed --class=RoleSeeder --force
  - php artisan test --parallel

Seed only what tests assume globally—usually minimal reference data. Let each test factory-create the rest. On GitLab CI pipelines I maintain with Deployer 7, production deploy runs migrate --force plus a small idempotent seeder class, never full demo seeding. See the step-by-step CI/CD pipeline with GitLab CI for Laravel for hook placement after symlink swap and PHP-FPM reload.

Anti-patterns to avoid

  • RefreshDatabase + full DatabaseSeeder — doubles migration time and hides test coupling.
  • Hard-coded user ID 1 in seeders that tests rely on—use role slugs or factory-created actors instead.
  • Duplicating factory logic inside seeders as raw arrays—call the factory or extract a shared static method.
  • Seeding PII-like fake emails to production — even Faker output does not belong on live MySQL 9.7 unless the environment is disposable.

Database choice matters at scale: whether you run MySQL 8.4 LTS or PostgreSQL 18, bulk factory inserts in tests stress different indexes. The PostgreSQL guide for Laravel developers covers transaction wrapping around test databases—useful when factories create deep graphs.

Seeding Gotchas vs FixesAvoidFull db:seed before every test500 factory users in productionDuplicate arrays in seedersTests assuming user id = 1Non-idempotent seed on deployMixing demo + live credentialsSlow CI, deploy failuresPreferMinimal RoleSeeder in CI onlyEnv-guarded demo seedersFactories as single sourceFactory-created actingAs userfirstOrCreate in prod seedersSeparate .env.testing databaseFast tests, safe deploys
Laravel seeders vs factories when to use: avoid full-database seeding in tests; prefer idempotent seeders and isolated factory setup.

Filament, APIs, and admin panels

Admin panels often need a bootstrap admin seeder once; everything else is operational data entered through the UI. If you use Laravel Filament, seed the super-admin role and permissions, then let staff create records live. API test suites should factory-create tokens via Sanctum rather than seeding OAuth clients globally—patterns covered in Laravel API best practices and policies and gates.

For Nepali businesses standardizing on Laravel, this split reduces support load after launch—predictable seeders mean fewer "works on my machine" calls during Dashain freeze periods when clients hesitate to deploy. That operational angle is why Laravel fits Nepali business constraints when tooling stays boring and explicit.

Key Takeaways

  • Use seeders for fixed reference, lookup, and bootstrap data your code depends on by slug, ID, or known value.
  • Use factories for tests and local bulk data where random plausible values are acceptable and isolation matters.
  • Combine them: production seeders stay idempotent; demo seeders call factories behind app()->environment() guards.
  • Never run full DatabaseSeeder before every test—seed minimally in CI, factory-create per test case.
  • Keep factory definitions as the single source of fake row shape; avoid copy-pasting the same arrays into seeders.
  • Document which seeders run on production deploy versus staging so ops and support and maintenance teams know what to expect.

People Also Ask

Can Laravel factories replace seeders entirely?

No. Factories generate variable data and are optimized for object creation in tests. Seeders express intentional datasets your application expects—default roles, settings, tax tables. Replacing seeders with factories in production would produce different rows on every deploy and break code that references stable slugs or IDs.

Should you run db:seed in production?

Run only small, idempotent seeders in production—typically after migrations during deploy. Use firstOrCreate or equivalent upsert logic. Avoid demo seeders and bulk factory creation. On Ubuntu servers running PHP 8.5 and Laravel 13, wire the seeder into your Deployer task or CI deploy step, not into artisan schedule.

What is the difference between DatabaseSeeder and individual seeders?

DatabaseSeeder is the entry point that calls other seeder classes in order. Individual seeders each own one concern—roles, settings, demo catalog. Splitting them keeps production deploys fast and lets CI invoke only RoleSeeder without dragging in demo data.

How do seeders work with Laravel model events and observers?

Both seeders and factories trigger Eloquent events unless you use Model::withoutEvents() or createQuietly(). If observers send emails or hit external APIs, seeding can cause side effects. Wrap noisy seeders in withoutEvents or disable specific observers during db:seed on staging.

Ship predictable data layers from day one

Getting Laravel seeders vs factories when to use right is cheap insurance: tests stay fast, staging stays readable, and production deploys stop failing on duplicate key errors. Seed the contract; factory the chaos. If you are planning a new Laravel 13 application—or untangling a legacy app where seeders and factories were mixed without plan—custom software development support can audit your migration and seeding pipeline before bad patterns reach production. For regex-heavy factory definitions or Faker edge cases, the regex tester on this site helps validate patterns offline. When you are ready to talk through architecture for your next project, contact us with your stack and deployment setup.

Frequently Asked Questions

Both populate your database through Eloquent, but they serve different jobs. A seeder is an orchestration class under database/seeders/ that inserts known rows—roles, tax rates, settings—via explicit inserts or model calls. A factory under database/factories/ is a blueprint that uses Faker to generate plausible but variable attributes each time. Seeders answer “load this exact contract data”; factories answer “give me a realistic record without caring about the specific values.”

Use seeders when the row itself matters, not just that a row exists. If randomizing or removing the data would break a feature, it belongs in a seeder. Typical cases: reference data like roles, permission names, order statuses, and document types; environment bootstrap such as a default admin on staging; idempotent production inserts for VAT rates or shipping zones; and cross-table baseline sets that must exist together before the app boots cleanly. Your code references stable slugs or IDs—seed those deliberately.

Factories win when you need shape without caring about content. Use them in PHPUnit or Pest tests to create isolated models—users, orders, bookings—without depending on global seed state another test might mutate. They also help prototyping UI locally with hundreds of products, stress-testing queries in disposable databases, and defining reusable states like unverified() or confirmed(). Each test should factory-create only what it needs. Avoid seeding the entire database before every test run; that coupling slows CI and hides dependencies between tests.

No. Factories generate variable data for tests and local dev. Seeders load intentional datasets your application expects—default roles, settings, lookup tables—with identical results when written correctly.

Run only small, idempotent seeders after migrations during deploy. Use firstOrCreate or upsert logic. Never run demo seeders or bulk factory creation on live databases.

DatabaseSeeder is the entry point that calls other seeder classes in order. Individual seeders each own one concern—roles, settings, demo catalog—keeping production deploys fast and letting CI invoke only what tests need.

The mature pattern is seeders calling factories only where bulk volume helps, while factories stand alone in tests. DatabaseSeeder orchestrates specialized seeders: fixed seeders like RoleSeeder and SettingsSeeder run everywhere; demo seeders calling Product::factory()->count(100) run only behind app()->environment('local', 'staging') guards. On an eCommerce app like Nepal Gift Card, seeders hold payment method enums and admin roles while factories generate synthetic orders in tests. Never ship unguarded User::factory()->count(500)->create() in DatabaseSeeder—it inflates staging storage and masks real performance issues.

Treat migrations, seeders, and factories as three separate pipeline concerns. A working GitLab CI test job runs migrate --force, seeds only minimal reference data such as RoleSeeder, then runs tests in parallel—letting each test factory-create the rest. Production deploys via Deployer 7 should run migrate --force plus a small idempotent seeder, never full demo seeding. Anti-patterns include RefreshDatabase combined with full DatabaseSeeder, which doubles migration time and hides test coupling. Seed globally only what tests truly assume; everything else belongs in per-test factory setup.

Production seeders must be safe to re-run after every deploy. Use firstOrCreate, updateOrCreate, or equivalent upsert logic instead of blind insert() calls that fail on unique constraints the second time. A RoleSeeder looping through admin, staff, and client with Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']) is the pattern I use on client portals with Spatie Laravel Permission. Wire the seeder into your Deployer task or CI deploy step after migrations, not into artisan schedule. Document which seeders run on production versus staging so ops teams know what to expect.

No. Running full DatabaseSeeder before every test suite is a common mistake that slows CI from minutes to tens of minutes on larger apps. Tests should factory-create their own data in isolation. In CI, seed only minimal reference data—typically a RoleSeeder—that tests genuinely assume globally. Pair RefreshDatabase with per-test factory setup, not with demo catalog seeders or bulk factory creation. Each test owning its data keeps failures localized and prevents one test mutating shared seed state that breaks another.

Seeders live under database/seeders/ as orchestration classes you register in DatabaseSeeder and invoke with php artisan db:seed or php artisan db:seed --class=RoleSeeder. Factories live under database/factories/ as definition classes describing model attributes. In Laravel 13.x, which requires PHP 8.3 or higher, you call factories from tests with User::factory()->create() or from seeders with User::factory()->count(50)->create(). Both features ship in the core framework documented at laravel.com/docs/13.x/seeding and laravel.com/docs/13.x/eloquent-factories. Keep this layout consistent so migrations, seeders, and factories stay predictable across local, CI, and production.

Call factories from seeders when you need bulk plausible volume but not fixed values—demo catalogs on staging, local UI prototyping with 100 products, or QA datasets that should look realistic without hand-maintaining arrays. Guard those seeders with environment checks so DemoCatalogSeeder never runs in production. Fixed contract data—roles, tax enums, payment methods—should stay as explicit seeder inserts, not factory output. Avoid duplicating factory logic as raw arrays inside seeders; call the factory or extract a shared static method so factory definitions remain the single source of fake row shape.

Both seeders and factories trigger Eloquent model events unless you bypass them with Model::withoutEvents() or createQuietly(). If observers send emails, queue jobs, or hit external APIs, seeding can cause unexpected side effects during db:seed on staging or CI. Wrap noisy seeders in withoutEvents or disable specific observers when bootstrapping reference data. This matters on legal-tech portals and booking apps where observers might fire notifications for every inserted row. Test whether your seeders behave correctly with observers enabled before wiring them into production deploy hooks.

Several mistakes compound across environments. Running RefreshDatabase plus full DatabaseSeeder doubles migration time and couples tests to global state. Hard-coding user ID 1 in seeders that tests rely on breaks when insert order changes—use role slugs or factory-created actors instead. Copy-pasting factory attribute arrays into seeders creates drift when columns change. Seeding PII-like fake emails to production MySQL, even via Faker, does not belong on live databases. Running unguarded bulk factory creation in DatabaseSeeder inflates staging and hides performance issues. Keep production seed paths fixed and idempotent; keep factories in tests and guarded demo seeders only.

Factory states let tests opt into specific variations without separate factory classes. A BookingFactory with a confirmed() state sets status to confirmed, so a Pest test can run Booking::factory()->confirmed()->create() and act as that booking's user in one readable chain. Relationship shortcuts like User::factory()->hasPosts(3) keep setup concise. afterCreating callbacks attach media, create pivot rows, or fire domain events—useful on booking apps where tests need supplier links and itinerary lines without duplicating seeder logic. For API test suites, factory-create Sanctum tokens rather than seeding OAuth clients globally. Factories belong in your testing workflow; seeders handle the fixed bootstrap your app code depends on.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: