# How Database Migrations Are Managed in Akash Console: A Complete Guide to Drizzle ORM

> Learn how Akash Console manages database migrations expertly with Drizzle ORM. Discover automatic application of pending changes ensuring seamless updates and data integrity. A complete guide.

- Repository: [Akash Network/console](https://github.com/akash-network/console)
- Tags: how-to-guide
- Published: 2026-02-24

---

**Akash Console manages database migrations using Drizzle ORM's built-in migrator, storing versioned SQL files in `apps/indexer/drizzle/` and automatically applying pending changes on application startup via the Postgres provider.**

Akash Console is a complex multi-application repository that relies on PostgreSQL for data persistence. To keep the database schema synchronized with the evolving codebase, the project implements a robust migration system using **Drizzle ORM**. This approach ensures that schema changes are version-controlled, reproducible, and automatically applied across production and test environments.

## Migration Architecture Overview

The migration system in Akash Console follows a file-based approach where each schema change is stored as a numbered SQL file. The **Drizzle ORM migrator** compares these files against a metadata table in the database (`drizzle_meta`) to determine which migrations have already been applied and which need to run.

This architecture separates the concerns of schema definition, migration generation, and runtime execution across different applications within the monorepo.

## Where Migration Files Are Stored

All migration files reside in the **indexer** application directory:

```

apps/indexer/drizzle/
├── 0000_chunky_rattler.sql
├── 0001_add_bseq_to_bid_and_lease.sql
├── 0002_add_provider_attributes.sql
└── ...

```

### SQL File Structure and Naming Convention

Migration files follow a strict numeric prefix pattern (`0000_`, `0001_`, etc.) that ensures sequential execution. Each file contains raw SQL statements that modify the database schema, such as `CREATE TABLE`, `ALTER TABLE`, or `CREATE INDEX` commands.

The Drizzle ORM migrator reads these files in alphabetical order, ensuring that dependencies between schema changes are respected.

## Configuration and Schema Definition

### Drizzle-Kit Configuration File

The migration generation process is controlled by [`apps/indexer/drizzle.config.ts`](https://github.com/akash-network/console/blob/main/apps/indexer/drizzle.config.ts). This configuration file specifies:

- The path to the TypeScript schema definition ([`schema.ts`](https://github.com/akash-network/console/blob/main/schema.ts))
- The output directory for generated SQL migrations
- Database connection parameters for introspection

When developers modify the schema in [`schema.ts`](https://github.com/akash-network/console/blob/main/schema.ts), they run the Drizzle-Kit CLI to generate new SQL migration files based on the differences between the current schema and the database state.

### Environment Configuration

The runtime migration system is configured through [`apps/api/src/core/config/env.config.ts`](https://github.com/akash-network/console/blob/main/apps/api/src/core/config/env.config.ts):

```typescript
DRIZZLE_MIGRATIONS_FOLDER: z.string().optional().default("./drizzle"),

```

This environment variable allows operators to override the default migrations folder location if needed, though the standard deployment uses the default path pointing to `apps/indexer/drizzle`.

## Runtime Migration Execution

### Automatic Migration on Startup

When the API service starts, it immediately runs pending migrations before accepting traffic. This ensures the database schema is always compatible with the application code version.

The migration logic resides in [`apps/api/src/core/providers/postgres.provider.ts`](https://github.com/akash-network/console/blob/main/apps/api/src/core/providers/postgres.provider.ts).

### The migrate() Function Implementation

The provider creates a dedicated migration client and invokes Drizzle's `migrate` function:

```typescript
// apps/api/src/core/providers/postgres.provider.ts
import { drizzle } from "drizzle-orm/postgres-js";
import { migrate } from "drizzle-orm/postgres-js/migrator";

const pgMigrationDatabase = drizzle(migrationClient, getDrizzleOptions(config));
await migrate(pgMigrationDatabase, {
  migrationsFolder: config.DRIZZLE_MIGRATIONS_FOLDER,
});

```

This code:
1. Creates a Drizzle client connected to PostgreSQL
2. Calls `migrate()` with the database instance and migrations folder path
3. Automatically applies any SQL files not yet recorded in `drizzle_meta`

The migration runs with a maximum of one connection (`{ max: 1 }`) to prevent conflicts during schema changes.

## Testing with Migrations

### Isolated Test Database Setup

Both the API and notifications services use migration-aware test helpers to ensure test databases match the production schema.

In [`apps/api/test/services/test-database.service.ts`](https://github.com/akash-network/console/blob/main/apps/api/test/services/test-database.service.ts):

```typescript
import { drizzle } from "drizzle-orm/postgres-js";
import { migrate } from "drizzle-orm/postgres-js/migrator";
import { join } from "path";

export async function createTestDatabase() {
  const migrationClient = postgres(process.env.TEST_POSTGRES_URI, { max: 1 });
  const pgMigrationDatabase = drizzle(migrationClient);

  const migrationsFolder = join(process.cwd(), "../indexer/drizzle");
  await migrate(pgMigrationDatabase, { migrationsFolder });

  return drizzle(migrationClient);
}

```

A similar pattern appears in [`apps/notifications/test/services/test-database.service.ts`](https://github.com/akash-network/console/blob/main/apps/notifications/test/services/test-database.service.ts). This ensures every test run starts with a fresh, fully-migrated database instance.

## Creating New Migrations

Developers generate new migrations using the Drizzle-Kit CLI. When schema changes are made to [`apps/indexer/drizzle/schema.ts`](https://github.com/akash-network/console/blob/main/apps/indexer/drizzle/schema.ts), run:

```bash

# From the repository root

npx drizzle-kit generate:pg --schema ./apps/indexer/drizzle/schema.ts \
  --out ./apps/indexer/drizzle \
  --migration-name add_new_column_to_user

```

This creates a new numbered SQL file (e.g., [`0006_add_new_column_to_user.sql`](https://github.com/akash-network/console/blob/main/0006_add_new_column_to_user.sql)) containing the necessary `ALTER TABLE` or `CREATE` statements. The file will be automatically applied on the next application startup.

## Summary

- **Migration files** are stored as versioned SQL in `apps/indexer/drizzle/` with numeric prefixes ensuring sequential execution.
- **Drizzle ORM** provides the migration engine, configured via [`drizzle.config.ts`](https://github.com/akash-network/console/blob/main/drizzle.config.ts) and executed through the `migrate()` function.
- **Automatic execution** occurs on every API startup via [`postgres.provider.ts`](https://github.com/akash-network/console/blob/main/postgres.provider.ts), ensuring schema consistency before traffic is served.
- **Environment configuration** in [`env.config.ts`](https://github.com/akash-network/console/blob/main/env.config.ts) allows customization of the migrations folder path via `DRIZZLE_MIGRATIONS_FOLDER`.
- **Test isolation** is maintained by running the same migration logic against temporary databases in both API and notifications test suites.

## Frequently Asked Questions

### What migration tool does Akash Console use?

Akash Console uses **Drizzle ORM** with its built-in **postgres-js migrator**. This tool compares migration files against the `drizzle_meta` table in PostgreSQL to determine which schema changes need to be applied, then executes the corresponding SQL files sequentially.

### Where are migration files stored in the repository?

Migration files are stored in `apps/indexer/drizzle/` as plain SQL files with numeric prefixes (e.g., [`0000_chunky_rattler.sql`](https://github.com/akash-network/console/blob/main/0000_chunky_rattler.sql), [`0001_add_bseq_to_bid_and_lease.sql`](https://github.com/akash-network/console/blob/main/0001_add_bseq_to_bid_and_lease.sql)). This location is referenced by the `DRIZZLE_MIGRATIONS_FOLDER` environment variable, which defaults to `./drizzle` in [`apps/api/src/core/config/env.config.ts`](https://github.com/akash-network/console/blob/main/apps/api/src/core/config/env.config.ts).

### How does Akash Console handle migrations in test environments?

Both the API and notifications services implement test database helpers that create isolated PostgreSQL instances and run the full migration suite before executing tests. The [`test-database.service.ts`](https://github.com/akash-network/console/blob/main/test-database.service.ts) files in `apps/api/test/services/` and `apps/notifications/test/services/` use the same `migrate()` function from Drizzle ORM to ensure test databases match the production schema exactly.

### Can I customize the migrations folder location?

Yes. While the default location is `./drizzle` (pointing to `apps/indexer/drizzle` in the standard deployment), you can override this by setting the `DRIZZLE_MIGRATIONS_FOLDER` environment variable in [`apps/api/src/core/config/env.config.ts`](https://github.com/akash-network/console/blob/main/apps/api/src/core/config/env.config.ts). This allows flexibility for custom deployment scenarios or monorepo restructuring.