Reader Stacks

Laravel Database Seeders: Creating and Running Them

A seeder fills the database with rows — for lookup data every environment needs, or for realistic fake data during local development. Which one it is changes how you should write it.

A migration defines the shape of a table; a seeder puts rows into it. Laravel treats seeding as a distinct concern from schema changes for a good reason — the data a seeder inserts is either data every environment genuinely needs (a default admin role, a list of countries) or throwaway fake data for local development, and those two cases call for different approaches.

1. Creating a seeder

php artisan make:seeder RoleSeeder
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class RoleSeeder extends Seeder
{
    public function run(): void
    {
        DB::table('roles')->insert([
            ['name' => 'admin'],
            ['name' => 'editor'],
            ['name' => 'viewer'],
        ]);
    }
}

2. Registering it in DatabaseSeeder

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

php artisan db:seed without arguments only runs DatabaseSeeder — any seeder not listed in its call() array never runs unless invoked individually. This registration step is easy to forget after generating a new seeder class.

3. Running seeders

php artisan db:seed                    # runs DatabaseSeeder (and whatever it calls)
php artisan db:seed --class=RoleSeeder # runs one specific seeder directly
php artisan migrate:fresh --seed       # drop everything, re-migrate, then seed

4. Seeding real lookup data vs. fake development data

These are genuinely different use cases and mixing them in one seeder gets confusing fast:

  • Lookup/reference data (roles, permissions, a fixed list of categories) — data every environment, including production, actually needs. Write this with plain DB::table()->insert() or Model::create() calls, with real, specific values.
  • Fake development data (500 sample orders to test pagination, realistic-looking test users) — never something you want in production. Use a model factory together with the seeder instead of hardcoding fake values by hand.
// UserSeeder — development-only fake data
public function run(): void
{
    \App\Models\User::factory()->count(50)->create();
}

5. Keeping seeders idempotent

Running db:seed a second time with a plain insert() call duplicates every row — for lookup data that should exist exactly once, use updateOrCreate() or firstOrCreate() instead, so re-running the seeder is safe:

foreach (['admin', 'editor', 'viewer'] as $name) {
    \App\Models\Role::firstOrCreate(['name' => $name]);
}

6. Never run migrate:fresh --seed against production

migrate:fresh drops every table before rebuilding them — it's meant for local development and CI, never a database with real user data. Seeding a production database (adding a new fixed lookup row, for instance) should go through a normal migration or a dedicated, carefully-reviewed one-off script instead.

Topics: Database Migrations