Use faker in laravel when inserting thousand of records
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Mastering Bulk Seeding in Laravel: Using Faker for Thousands of Records Without Hitting Database Limits
As a senior developer working with large-scale applications, one of the most common challenges we face during development and testing is generating realistic, high-volume data. When you need to populate your database with thousands or millions of records, relying on manual loops and custom logic often leads to performance bottlenecks and frustrating database errors.
This post dives into the issues you encountered while trying to scale your Laravel seeding process using Faker and Factories, and provides a robust, scalable solution for inserting massive datasets efficiently. We will address why simple looping fails under heavy load and demonstrate the best practices for bulk data creation in Laravel.
The Pitfall of Procedural Looping in Seeding
You correctly identified that simply iterating and calling $model::create() or using Post::insert($chunk) is faster than row-by-row insertion. However, when dealing with tens of thousands of records, even optimized batch inserts can hit database connection limits or query timeouts, resulting in errors like MySQL server has gone away.
This error typically stems from the sheer volume of operations overwhelming the database connection pool or exceeding the configured execution time for a single large transaction. Furthermore, attempting to manually inject Faker data into these bulk operations often leads to logical errors regarding foreign key relationships or data integrity.
The goal in Laravel seeding is not just speed; it is correctness and maintainability. We want our seeders to act as declarative instructions for the database state, not complex procedural scripts.
The Correct Approach: Leveraging Factories for Bulk Data Generation
The most idiomatic and safest way to generate large sets of data in Laravel is by fully utilizing Model Factories. Factories are designed to abstract the creation logic, ensuring that your generated data adheres to the defined structure and relationships of your models.
Instead of manually looping and building arrays, we should let the factory system handle the iteration, which often results in more optimized database interactions handled internally by Eloquent.
Step 1: Define Factory Relationships Correctly
Your initial setup showed a good understanding of setting up foreign keys via factories (e.g., $users->random()). This is crucial for data integrity. When seeding thousands of posts, you must ensure those posts link to valid users.
If you are aiming for bulk creation, focus on generating the necessary parent records first, and then use mass assignment or factory methods for the children.
// Example Factory refinement (PostFactory.php)
public function definition()
{
return [
'body' => $this->faker->text(200),
'image_path' => $this->faker->imageUrl(640, 480, 'nature'), // Use specific Faker methods
// The relationship setup remains vital for data integrity
'user_id' => $this->state(fn() => User::factory()->create()->id),
];
}
Scaling Up: Optimized Seeding Strategies
For inserting thousands of records, especially when dealing with relationships, we need strategies that minimize database overhead.
Strategy 1: Using create or createMany (Where Applicable)
If you are creating many related records, ensure your factory is set up to handle this. For truly massive inserts, the underlying Eloquent methods are often optimized better than raw procedural code. Remember that understanding Eloquent relationships is key when dealing with data structures in Laravel, as discussed on the official documentation at https://laravelcompany.com.
Strategy 2: The "Seed and Fetch" Method for Massive Scale
When hitting database timeouts during massive inserts, a more robust approach is to separate the generation of IDs from the final insertion step, or use batching aggressively with explicit transactions.
If you must generate 50,000 posts linked to existing users, generating the user IDs first and then using those in a controlled loop is safer than relying on random selections within an immediate bulk insert operation that might time out:
public function run()
{
// 1. Pre-generate necessary parent data (e.g., Users)
$users = User::factory()->count(5000)->create();
$postData = [];
// 2. Generate data points iteratively, ensuring valid foreign keys
for ($i = 0; $i < 50000; $i++) {
$postData[] = [
'body' => $this->faker->text(150),
'image_path' => $this->faker->imageUrl(),
// Select a random user ID from the pre-generated set
'user_id' => $users->random()->id,
'created_at' => now()->subMinutes($i), // Sequential timestamps for better testing
];
}
// 3. Perform bulk insertion (This is often faster if handled correctly)
DB::table('posts')->insert($postData);
}
By separating the data generation (using Faker and PHP arrays) from the database write operation, you gain control over the flow. Using DB::table()->insert() directly bypasses some of Eloquent's overhead for massive inserts, which can mitigate the "server has gone away" error, provided you manage your transactions effectively.
Conclusion
Mastering large-scale data seeding in Laravel requires a shift in mindset from procedural scripting to declarative factory usage. When dealing with thousands of records:
- Prioritize Data Integrity: Ensure your factories correctly handle foreign key relationships before attempting bulk inserts.
- Optimize for Performance: For extreme volumes, consider separating large-scale data generation (using Faker within PHP loops) from the final database insertion step, utilizing raw
DBfacade commands when necessary to manage connection timeouts effectively.
By adopting these principles, you ensure your seeding process is not only fast but also reliable and reflective of best practices in building scalable applications on Laravel.