diff --git a/adonis-typings/migrator.ts b/adonis-typings/migrator.ts
index 59654744..94b10209 100644
--- a/adonis-typings/migrator.ts
+++ b/adonis-typings/migrator.ts
@@ -15,10 +15,10 @@ declare module '@ioc:Adonis/Lucid/Migrator' {
    * Migration node returned by the migration source
    * implementation
    */
-  export type MigrationNode = {
+  export type FileNode<T extends any> = {
     absPath: string,
     name: string,
-    source: SchemaConstructorContract,
+    source: T,
   }
 
   /**
@@ -41,7 +41,7 @@ declare module '@ioc:Adonis/Lucid/Migrator' {
   export type MigratedFileNode = {
     status: 'completed' | 'error' | 'pending',
     queries: string[],
-    migration: MigrationNode,
+    migration: FileNode<SchemaConstructorContract>,
     batch: number,
   }
 
diff --git a/adonis-typings/seeds.ts b/adonis-typings/seeds.ts
new file mode 100644
index 00000000..f8fbe4d9
--- /dev/null
+++ b/adonis-typings/seeds.ts
@@ -0,0 +1,34 @@
+/*
+* @adonisjs/lucid
+*
+* (c) Harminder Virk <virk@adonisjs.com>
+*
+* For the full copyright and license information, please view the LICENSE
+* file that was distributed with this source code.
+*/
+
+declare module '@ioc:Adonis/Lucid/Seeder' {
+  import { QueryClientContract } from '@ioc:Adonis/Lucid/Database'
+
+  /**
+   * Shape of seeder class
+   */
+  export type SeederConstructorContract = {
+    developmentOnly: boolean,
+    client: QueryClientContract,
+    new (client: QueryClientContract): {
+      run (): Promise<void>
+    }
+  }
+
+  /**
+   * Shape of file node returned by the run method
+   */
+  export type SeederFileNode = {
+    absPath: string,
+    name: string,
+    source: SeederConstructorContract,
+    status: 'pending' | 'completed' | 'failed' | 'ignored',
+    error?: any,
+  }
+}
diff --git a/commands/DbSeed.ts b/commands/DbSeed.ts
new file mode 100644
index 00000000..c3670b48
--- /dev/null
+++ b/commands/DbSeed.ts
@@ -0,0 +1,155 @@
+/*
+ * @adonisjs/lucid
+ *
+ * (c) Harminder Virk <virk@adonisjs.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+*/
+
+import { inject } from '@adonisjs/fold'
+import { SeederFileNode } from '@ioc:Adonis/Lucid/Seeder'
+import { BaseCommand, Kernel, flags } from '@adonisjs/ace'
+import { DatabaseContract } from '@ioc:Adonis/Lucid/Database'
+import { ApplicationContract } from '@ioc:Adonis/Core/Application'
+
+@inject([null, null, 'Adonis/Lucid/Database'])
+export default class DbSeed extends BaseCommand {
+  public static commandName = 'db:seed'
+  public static description = 'Execute database seeder files'
+
+  /**
+   * Choose a custom pre-defined connection. Otherwise, we use the
+   * default connection
+   */
+  @flags.string({ description: 'Define a custom database connection for the seeders', alias: 'c' })
+  public connection: string
+
+  /**
+   * Interactive mode allows selecting seeder files
+   */
+  @flags.boolean({ description: 'Run seeders in interactive mode', alias: 'i' })
+  public interactive: boolean
+
+  /**
+   * Define a custom set of seeder files. Interactive and files together ignores
+   * the interactive mode.
+   */
+  @flags.array({ description: 'Define a custom set of seeders files names to run', alias: 'f' })
+  public files: string[]
+
+  /**
+   * This command loads the application, since we need the runtime
+   * to find the migration directories for a given connection
+   */
+  public static settings = {
+    loadApp: true,
+  }
+
+  constructor (app: ApplicationContract, kernel: Kernel, private db: DatabaseContract) {
+    super(app, kernel)
+  }
+
+  /**
+   * Print log message to the console
+   */
+  private printLogMessage (file: SeederFileNode) {
+    const colors = this['colors']
+
+    let color: keyof typeof colors = 'gray'
+    let message: string = ''
+    let prefix: string = ''
+
+    switch (file.status) {
+      case 'pending':
+        message = 'pending  '
+        color = 'gray'
+        break
+      case 'failed':
+        message = 'error    '
+        prefix = file.error!.message
+        color = 'red'
+        break
+      case 'ignored':
+        message = 'ignored  '
+        prefix = 'Enabled only in development environment'
+        color = 'dim'
+        break
+      case 'completed':
+        message = 'completed'
+        color = 'green'
+        break
+    }
+
+    console.log(`${colors[color]('❯')} ${colors[color](message)} ${file.name}`)
+    if (prefix) {
+      console.log(`  ${colors[color](prefix)}`)
+    }
+  }
+
+  /**
+   * Execute command
+   */
+  public async handle (): Promise<void> {
+    const client = this.db.connection(this.connection || this.db.primaryConnectionName)
+
+    /**
+     * Ensure the define connection name does exists in the
+     * config file
+     */
+    if (!client) {
+      this.logger.error(
+        `${this.connection} is not a valid connection name. Double check config/database file`,
+      )
+      return
+    }
+
+    const { SeedsRunner } = await import('../src/SeedsRunner')
+    const runner = new SeedsRunner(this.application.seedsPath(), process.env.NODE_ENV === 'development')
+
+    /**
+     * List of available files
+     */
+    const files = await runner.listSeeders()
+
+    /**
+     * List of selected files. Initially, all files are selected and one can
+     * define cherry pick using the `--interactive` or `--files` flag.
+     */
+    let selectedFileNames: string[] = files.map(({ name }) => name)
+
+    if (this.files.length) {
+      selectedFileNames = this.files
+      if (this.interactive) {
+        this.logger.warn('Cannot use "--interactive" and "--files" together. Ignoring "--interactive"')
+      }
+    } else if (this.interactive) {
+      selectedFileNames = await this.prompt.multiple('Select files to run', files.map((file) => {
+        return {
+          disabled: file.status === 'ignored',
+          name: file.name,
+          hint: file.status === 'ignored' ? '(Enabled only in development environment)' : '',
+        }
+      }))
+    }
+
+    /**
+     * Execute selected seeders
+     */
+    for (let fileName of selectedFileNames) {
+      const sourceFile = files.find(({ name }) => fileName === name)
+      if (!sourceFile) {
+        this.printLogMessage({
+          name: fileName,
+          status: 'failed',
+          error: new Error('Invalid file path. Pass relative path from the "database/seeds" directory'),
+          source: {} as any,
+          absPath: fileName,
+        })
+      } else {
+        await runner.run(sourceFile, client)
+        this.printLogMessage(sourceFile)
+      }
+    }
+  }
+}
diff --git a/commands/MakeSeeder.ts b/commands/MakeSeeder.ts
new file mode 100644
index 00000000..8329923d
--- /dev/null
+++ b/commands/MakeSeeder.ts
@@ -0,0 +1,46 @@
+/*
+ * @adonisjs/lucid
+ *
+ * (c) Harminder Virk <virk@adonisjs.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+*/
+
+import { join } from 'path'
+import { BaseCommand, args } from '@adonisjs/ace'
+
+export default class MakeSeeder extends BaseCommand {
+  public static commandName = 'make:seeder'
+  public static description = 'Make a new Seeder file'
+
+  /**
+   * The name of the seeder file.
+   */
+  @args.string({ description: 'Name of the seeder class' })
+  public name: string
+
+  /**
+   * Execute command
+   */
+  public async handle (): Promise<void> {
+    const stub = join(
+      __dirname,
+      '..',
+      'templates',
+      'seeder.txt',
+    )
+
+    const path = this.application.seedsPath()
+
+    this
+      .generator
+      .addFile(this.name, { pattern: 'pascalcase', form: 'singular' })
+      .stub(stub)
+      .destinationDir(path || 'database/Seeders')
+      .useMustache()
+      .appRoot(this.application.cliCwd || this.application.appRoot)
+
+    await this.generator.run()
+  }
+}
diff --git a/commands/index.ts b/commands/index.ts
index 74d84e21..ff41456f 100644
--- a/commands/index.ts
+++ b/commands/index.ts
@@ -8,8 +8,10 @@
 */
 
 export default [
+  '@adonisjs/lucid/build/commands/DbSeed',
   '@adonisjs/lucid/build/commands/MakeModel',
   '@adonisjs/lucid/build/commands/MakeMigration',
+  '@adonisjs/lucid/build/commands/MakeSeeder',
   '@adonisjs/lucid/build/commands/Migration/Run',
   '@adonisjs/lucid/build/commands/Migration/Rollback',
   '@adonisjs/lucid/build/commands/Migration/Status',
diff --git a/package.json b/package.json
index 394da50a..8cfb9b6a 100644
--- a/package.json
+++ b/package.json
@@ -45,8 +45,8 @@
     "@poppinss/utils": "^2.2.6",
     "@types/faker": "^4.1.12",
     "cli-table3": "^0.6.0",
-    "fast-deep-equal": "^3.1.1",
     "faker": "^4.1.0",
+    "fast-deep-equal": "^3.1.1",
     "kleur": "^3.0.3",
     "knex": "^0.21.1",
     "knex-dynamic-connection": "^1.0.5",
diff --git a/src/Migrator/MigrationSource.ts b/src/Migrator/MigrationSource.ts
index ff7bada8..30f3748e 100644
--- a/src/Migrator/MigrationSource.ts
+++ b/src/Migrator/MigrationSource.ts
@@ -10,9 +10,10 @@
 /// <reference path="../../adonis-typings/index.ts" />
 
 import { join, isAbsolute, extname } from 'path'
+import { FileNode } from '@ioc:Adonis/Lucid/Migrator'
 import { esmRequire, fsReadAll } from '@poppinss/utils'
-import { MigrationNode } from '@ioc:Adonis/Lucid/Migrator'
 import { ConnectionConfig } from '@ioc:Adonis/Lucid/Database'
+import { SchemaConstructorContract } from '@ioc:Adonis/Lucid/Schema'
 import { ApplicationContract } from '@ioc:Adonis/Core/Application'
 
 /**
@@ -29,7 +30,7 @@ export class MigrationSource {
    * Returns an array of files inside a given directory. Relative
    * paths are resolved from the project root
    */
-  private getDirectoryFiles (directoryPath: string): Promise<MigrationNode[]> {
+  private getDirectoryFiles (directoryPath: string): Promise<FileNode<SchemaConstructorContract>[]> {
     const basePath = this.app.appRoot
 
     return new Promise((resolve, reject) => {
diff --git a/src/Migrator/index.ts b/src/Migrator/index.ts
index db00e012..f6d0a342 100644
--- a/src/Migrator/index.ts
+++ b/src/Migrator/index.ts
@@ -14,7 +14,7 @@ import { Exception } from '@poppinss/utils'
 import { ApplicationContract } from '@ioc:Adonis/Core/Application'
 
 import {
-  MigrationNode,
+  FileNode,
   MigratorOptions,
   MigratedFileNode,
   MigratorContract,
@@ -26,6 +26,7 @@ import {
   QueryClientContract,
   TransactionClientContract,
 } from '@ioc:Adonis/Lucid/Database'
+import { SchemaConstructorContract } from '@ioc:Adonis/Lucid/Schema'
 
 import { MigrationSource } from './MigrationSource'
 
@@ -182,7 +183,7 @@ export class Migrator extends EventEmitter implements MigratorContract {
    * Executes a given migration node and cleans up any created transactions
    * in case of failure
    */
-  private async executeMigration (migration: MigrationNode) {
+  private async executeMigration (migration: FileNode<SchemaConstructorContract>) {
     const client = await this.getClient(migration.source.disableTransactions)
 
     try {
diff --git a/src/SeedsRunner/index.ts b/src/SeedsRunner/index.ts
new file mode 100644
index 00000000..473ccbc0
--- /dev/null
+++ b/src/SeedsRunner/index.ts
@@ -0,0 +1,81 @@
+/*
+* @adonisjs/lucid
+*
+* (c) Harminder Virk <virk@adonisjs.com>
+*
+* For the full copyright and license information, please view the LICENSE
+* file that was distributed with this source code.
+*/
+
+import { join, extname } from 'path'
+import { esmRequire, fsReadAll } from '@poppinss/utils'
+import { SeederFileNode } from '@ioc:Adonis/Lucid/Seeder'
+import { QueryClientContract } from '@ioc:Adonis/Lucid/Database'
+
+/**
+ * Seeds runner exposes the API to list files from the seeds and
+ * also run a collection of seeders
+ */
+export class SeedsRunner {
+  constructor (
+    private seedsDir: string,
+    private isInDevelopment: boolean,
+  ) {}
+
+  /**
+   * Returns an array of files inside a given directory. Relative
+   * paths are resolved from the project root
+   */
+  public listSeeders (): Promise<SeederFileNode[]> {
+    return new Promise((resolve, reject) => {
+      const files = fsReadAll(this.seedsDir)
+      try {
+        resolve(files.sort().map((file) => {
+          const source = esmRequire(join(this.seedsDir, file))
+          const ignored = source.developmentOnly && !this.isInDevelopment
+
+          return {
+            absPath: join(this.seedsDir, file),
+            name: file.replace(RegExp(`${extname(file)}$`), ''),
+            source: esmRequire(join(this.seedsDir, file)),
+            status: ignored ? 'ignored' : 'pending',
+          }
+        }))
+      } catch (error) {
+        reject(error)
+      }
+    })
+  }
+
+  /**
+   * Returns an array of files inside a given directory. Relative
+   * paths are resolved from the project root
+   */
+  public async run (
+    seeder: SeederFileNode,
+    client: QueryClientContract,
+  ): Promise<SeederFileNode> {
+    /**
+     * Ignore when running in non-development environment and seeder is development
+     * only
+     */
+    if (seeder.source.developmentOnly && !this.isInDevelopment) {
+      return seeder
+    }
+
+    try {
+      const seederInstance = new seeder.source(client)
+      if (typeof (seederInstance.run) !== 'function') {
+        throw new Error(`Missing method "run" on "${seeder.name}" seeder`)
+      }
+
+      await seederInstance.run()
+      seeder.status = 'completed'
+    } catch (error) {
+      seeder.status = 'failed'
+      seeder.error = error
+    }
+
+    return seeder
+  }
+}
diff --git a/templates/seeder.txt b/templates/seeder.txt
new file mode 100644
index 00000000..06a4262b
--- /dev/null
+++ b/templates/seeder.txt
@@ -0,0 +1,10 @@
+import { QueryClientContract } from '@ioc:Adonis/Lucid/Database'
+
+export default class {{ filename }} {
+  constructor (protected client: QueryClientContract) {
+  }
+
+  public run () {
+    // Write your database queries inside the run method
+  }
+}
diff --git a/test/seeds/seeder.spec.ts b/test/seeds/seeder.spec.ts
new file mode 100644
index 00000000..d12ec98f
--- /dev/null
+++ b/test/seeds/seeder.spec.ts
@@ -0,0 +1,144 @@
+/*
+* @adonisjs/lucid
+*
+* (c) Harminder Virk <virk@adonisjs.com>
+*
+* For the full copyright and license information, please view the LICENSE
+* file that was distributed with this source code.
+*/
+
+import test from 'japa'
+import { join } from 'path'
+import { Filesystem } from '@poppinss/dev-utils'
+import { SeedsRunner } from '../../src/SeedsRunner'
+import { getDb, setup, cleanup } from '../../test-helpers'
+
+const fs = new Filesystem(join(__dirname, 'app'))
+let db: ReturnType<typeof getDb>
+
+test.group('Seeder', (group) => {
+  group.before(async () => {
+    db = getDb()
+    await setup()
+  })
+
+  group.after(async () => {
+    await cleanup()
+    await db.manager.closeAll()
+  })
+
+  group.afterEach(async () => {
+    await fs.cleanup()
+  })
+
+  test('get list of seed files recursively', async (assert) => {
+    const runner = new SeedsRunner(fs.basePath, false)
+    await fs.add('User.ts', '')
+    await fs.add('Tenant/User.ts', '')
+    await fs.add('Country/Post.ts', '')
+
+    const files = await runner.listSeeders()
+    assert.deepEqual(files, [
+      {
+        absPath: join(fs.basePath, 'Country/Post.ts'),
+        name: 'Country/Post',
+        source: {} as any,
+        status: 'pending',
+      },
+      {
+        absPath: join(fs.basePath, 'Tenant/User.ts'),
+        name: 'Tenant/User',
+        source: {} as any,
+        status: 'pending',
+      },
+      {
+        absPath: join(fs.basePath, 'User.ts'),
+        name: 'User',
+        source: {} as any,
+        status: 'pending',
+      },
+    ])
+  })
+
+  test('only pick .ts/.js files', async (assert) => {
+    const runner = new SeedsRunner(fs.basePath, false)
+    await fs.add('User.ts', '')
+    await fs.add('Tenant/User.ts', '')
+    await fs.add('Country/Post.ts', '')
+    await fs.add('foo.bar', '')
+    await fs.add('foo.js', '')
+
+    const files = await runner.listSeeders()
+    assert.deepEqual(files, [
+      {
+        absPath: join(fs.basePath, 'Country/Post.ts'),
+        name: 'Country/Post',
+        source: {} as any,
+        status: 'pending',
+      },
+      {
+        absPath: join(fs.basePath, 'Tenant/User.ts'),
+        name: 'Tenant/User',
+        source: {} as any,
+        status: 'pending',
+      },
+      {
+        absPath: join(fs.basePath, 'User.ts'),
+        name: 'User',
+        source: {} as any,
+        status: 'pending',
+      },
+      {
+        absPath: join(fs.basePath, 'foo.js'),
+        name: 'foo',
+        source: {} as any,
+        status: 'pending',
+      },
+    ])
+  })
+
+  test('run a seeder file', async (assert) => {
+    const runner = new SeedsRunner(fs.basePath, false)
+    await fs.add('User.ts', `export default class FooSeeder {
+      public static invoked = false
+
+      run () {
+        (this.constructor as any).invoked = true
+      }
+    }`)
+
+    const files = await runner.listSeeders()
+    const report = await runner.run(files[0], db.connection())
+    assert.equal(report.source['invoked'], true)
+    assert.equal(report.status, 'completed')
+  })
+
+  test('catch and return seeder errors', async (assert) => {
+    const runner = new SeedsRunner(fs.basePath, false)
+    await fs.add('User.ts', `export default class FooSeeder {
+      run () {
+        throw new Error('Failed')
+      }
+    }`)
+
+    const files = await runner.listSeeders()
+    const report = await runner.run(files[0], db.connection())
+    assert.equal(report.status, 'failed')
+    assert.exists(report.error)
+  })
+
+  test('mark file as ignored when "developmentOnly = true" and not running in development mode', async (assert) => {
+    const runner = new SeedsRunner(fs.basePath, false)
+    await fs.add('User.ts', `export default class FooSeeder {
+      public static invoked = false
+      public static developmentOnly = true
+
+      run () {
+        (this.constructor as any).invoked = true
+      }
+    }`)
+
+    const files = await runner.listSeeders()
+    assert.equal(files[0].status, 'ignored')
+  })
+})