Skip to main content

Storage overview

Storage is the persistence layer for the Mastra runtime. It keeps memory, workflow state, observability data, eval results, schedules, and long-running agent state available after a process restarts.

Storage powers:

  • Memory: Message history, threads, resources, and working memory.
  • Workflows: Durable snapshots for suspended and resumed workflow runs.
  • Observability: Traces, spans, metrics, logs, and feedback.
  • Evals: Scores, datasets, experiments, and evaluation results.
  • Long-running agents: Background tasks, schedules, goals, and thread state.

When to configure storage
Direct link to When to configure storage

Configure a persistent storage adapter when state must survive restarts, be shared across processes, or be visible in Studio across sessions. The default in-memory store is useful for tests and short local experiments, but it loses data when the process exits.

Use storage when your application needs any of these behaviors:

  • Agents remember past messages or user facts.
  • Workflows suspend and resume after a restart.
  • Traces, metrics, logs, scores, or feedback stay available for analysis.
  • Schedules and background tasks continue across deployments.
  • Multiple runtime processes read and write the same state.

How storage works
Direct link to How storage works

Mastra storage is organized into domains. A domain owns one type of runtime data, and a storage adapter implements one or more domains.

DomainWhat it stores
memoryThreads, messages, resources, working memory, and other agent memory state.
workflowsWorkflow snapshots used to suspend and resume runs.
observabilityTraces, spans, metrics, logs, and feedback.
scoresEval score records.
datasetsDataset records and dataset items used by evals and experiments.
experimentsExperiment runs and per-item experiment results.
backgroundTasksBackground task records and execution state.
schedulesSchedule definitions and trigger history.
threadStateDurable task, goal, and thread state.

Adapter support varies by domain. For the full domain list and built-in schemas, see the storage overview reference.

Choose a backend by data shape
Direct link to Choose a backend by data shape

Different domains write and query different kinds of data. Pick a backend based on the domain's access pattern:

  • memory: Reads and writes rows during every remembered agent call. Use a transactional database such as libSQL, PostgreSQL, or MongoDB.
  • observability: Writes high-volume telemetry and often queries aggregations. Use a dedicated observability store or an online analytical processing (OLAP) backend such as ClickHouse or DuckDB.
  • workflows: Stores durable snapshots that must be available when a run resumes. Use a reliable persistent database.
  • scores, datasets, and experiments: Store lower-frequency evaluation data that's often read later for analysis.
  • schedules: Stores schedule definitions and fire history. Use an adapter that implements the schedules domain.

When domains have different operational needs, use composite storage to route each domain to the right backend.

Get started locally
Direct link to Get started locally

For local development, use libSQL with a file-backed database. It doesn't require a separate database server and persists state between restarts.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { LibSQLStore } from '@mastra/libsql'

export const mastra = new Mastra({
storage: new LibSQLStore({
id: 'mastra-storage',
url: 'file:./mastra.db',
}),
})
Sharing the database with Studio

When running mastra dev alongside your application, use an absolute path so both processes access the same database:

url: 'file:/absolute/path/to/your/project/mastra.db'

Relative paths like file:./mastra.db resolve based on each process's working directory, which may differ.

Mastra initializes the required storage structures on first use.

Configure for production
Direct link to Configure for production

For production, use a persistent managed database. PostgreSQL is a good default for most teams because it works well for transactional runtime state and is widely available as a managed service.

Production guidance:

  • Use a managed database with backups, monitoring, and connection pooling.
  • Keep local file databases such as file:./mastra.db out of multi-process production deployments.
  • Route high-volume domains, especially observability, to a dedicated backend with composite storage.
  • Configure retention policies on the storage adapter or composite store, then call storage.prune() from a scheduler or maintenance job.
  • Choose providers based on the domains your application uses. For example, schedules require an adapter that implements the schedules domain.

Configuration scope
Direct link to Configuration scope

Storage can be configured at the Mastra instance level or at the agent level.

Instance-level storage
Direct link to Instance-level storage

Instance-level storage is shared by agents, workflows, observability, evals, schedules, and other runtime features registered on the same Mastra instance.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { PostgresStore } from '@mastra/pg'

export const mastra = new Mastra({
storage: new PostgresStore({
id: 'mastra-storage',
connectionString: process.env.DATABASE_URL,
}),
})

Use instance-level storage when most runtime domains can share the same database.

Agent-level storage
Direct link to Agent-level storage

Agent-level storage is configured on a Memory instance. It overrides instance-level storage for that agent's memory data only.

src/mastra/agents/support-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { PostgresStore } from '@mastra/pg'

export const supportAgent = new Agent({
id: 'support-agent',
name: 'Support agent',
instructions: 'Answer customer support questions.',
model: 'openai/gpt-5.5',
memory: new Memory({
storage: new PostgresStore({
id: 'support-agent-storage',
connectionString: process.env.SUPPORT_AGENT_DATABASE_URL,
}),
}),
})

Use agent-level storage when an agent needs an isolated memory boundary or a different memory backend.

Composite storage
Direct link to Composite storage

MastraCompositeStore routes domains to different backends. Use it when one database isn't the right fit for every domain.

The following example uses libSQL as the default store and routes workflow state to PostgreSQL:

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { MastraCompositeStore } from '@mastra/core/storage'
import { LibSQLStore } from '@mastra/libsql'
import { WorkflowsPG } from '@mastra/pg'

export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite-storage',
default: new LibSQLStore({
id: 'default-storage',
url: 'file:./mastra.db',
}),
domains: {
workflows: new WorkflowsPG({
connectionString: process.env.DATABASE_URL,
}),
},
}),
})

You can also route observability to a dedicated analytics backend. See observability storage for an observability-specific example.

Supported providers
Direct link to Supported providers

Each provider page includes installation instructions, configuration parameters, and usage examples:

tip

libSQL is the fastest path for local development because it doesn't require running a separate database server.

Next steps
Direct link to Next steps