---
title: Manage database migrations
canonical: "https://agentbarn.dev/guides/self-hosting/migrations"
pubDate: "2026-08-29T00:00:00.000Z"
updatedDate: "2026-09-05T10:11:30.000Z"
author: Agent Barn
description: "Create, review, apply, verify, and recover Agent Barn migrations for Product, Ingest, Communications, Connection data, and PostgreSQL-backed delivery."
tags: [Self-hosting, How-to, "Platform engineers, self-hosted operators, and Agent Barn contributors", database, migrations, Alembic, PostgreSQL, Communications, Communication Connections, Kubernetes, rollback, schema, production, self-hosting]
categories: [Guides, Self-hosting]
---

Migration outcome

## A reviewed, tested, and recoverable schema change

Complete this workflow before applying an Agent Barn application database migration to a shared environment.

-   The repository has one continuous Alembic revision graph.
-   The migration has been reviewed against realistic PostgreSQL data.
-   The application image, schema, contents, and backup form one release contract.
-   Staging verification and a tested production recovery path are complete.

## Overview

Agent Barn uses Alembic with SQLModel metadata to version the schema of its application PostgreSQL database.

Model changeMigrationReviewTestStagingBackupProductionVerify

Migration files live in `api/migrations/versions/`. The migration environment in `api/migrations/env.py` imports the domain models before exposing their shared `SQLModel.metadata`.

Alembic normally connects through `DB_CONNECTION_URL`. The target database therefore matters whenever you inspect, generate, apply, or reverse a migration.

**One release contract**

A successful Helm rollback does not undo an Alembic migration. Treat the application image, database schema, database contents, and recovery backup as one release contract.

## What Agent Barn migrates

The self-hosted stack contains multiple PostgreSQL databases. They do not share one migration system.

| Database | Schema owner | Migrated by Agent Barn Alembic |
| --- | --- | --- |
| Agent Barn application PostgreSQL | Agent Barn API | Yes |
| LiteLLM PostgreSQL | LiteLLM | No |
| Firecrawl PostgreSQL | Firecrawl | No |

The commands in this guide affect only the database referenced by the API’s `DB_CONNECTION_URL`.

**Confirm the target database**

A locally executed Alembic command can modify staging or production when its connection URL points there. Confirm the target environment and database before every migration command.

Agent Barn Alembic covers Organizations and Memberships, Agents and Agent Access, Templates and Skills, Agent Secrets and Shared Credentials, Communication Connections, Communication Deliveries, canonical Conversation Messages, the Communications operational journal, Tool Calls, Domain Events and Event Deliveries, costs, and related operational records. Communications uses this shared application schema; it has no separate migration system.

## Communications data and legacy transition

Current Communications persistence includes records equivalent to `communication_connection`, `communication_delivery`, `communication_journal_entry`, and `agent_chat_message` with required `connection_id`. Each Connection belongs to one Agent and Organization; each Delivery belongs to one Connection. Canonical Conversation Messages carry their source Connection, and location identity includes Connection ID and channel ID.

Provider message identity is unique within a Connection. Retired Connections are preserved so Conversation history remains attributable, while Connection credentials remain encrypted. This is operational guidance, not a complete schema reference.

**Back up before crossing the Connection boundary**

Canonical Conversation Messages now require a Connection ID. Messages associated with migrated Platform configuration are backfilled to the generated Connection; messages that cannot be associated with a generic Connection cannot satisfy the new identity contract. The migration removes remaining null-Connection rows before enforcing the non-null constraint, so every historical message cannot always be retained.

### Legacy single-Platform Agents

For installations upgrading from the removed single-Platform Agent architecture, the transition creates generic Communication Connections, converts supported legacy Platform configuration into Connection settings and encrypted credentials, backfills the generated Connection ID onto compatible Conversation Messages, and creates Connection-scoped indexes and constraints. Later revisions remove legacy Agent Platform fields, Platform-specific configuration tables, and the obsolete per-user Slack configuration-token table. New Agents are headless and do not have a `platform` field.

Legacy deployments may contain duplicate provider credentials on different Agents. Where required, migration preserves otherwise valid legacy records using a legacy-scoped compatibility identity; newly created Connections remain subject to current provider-identity uniqueness rules. Do not manually duplicate new provider credentials after migration.

## Before you begin

You need a checkout of the `agent-barn` repository, a reachable PostgreSQL database, authorization to change it, and a repository-root `.env` with the correct `DB_CONNECTION_URL`. Prepare a tested backup and a maintenance or recovery plan before an incompatible production change.

Install the API dependencies from the repository root:

```
cd api
uv sync
cd ..
```

For local host commands, ensure `DB_CONNECTION_URL` is reachable from the host. Commands run through `./run.sh` use the Docker Compose database configuration instead.

### Choose a migration strategy

| Change | Examples | Recommended approach |
| --- | --- | --- |
| Additive | Nullable column, new table, compatible index | One release may be sufficient after testing |
| Data-transforming | Backfill, normalization, identifier conversion | Test representative data and verify row-level results |
| Destructive | Drop or rename a column, remove an enum value | Use an expand/contract sequence across releases |
| Long-running | Large backfill, table rewrite, blocking index | Measure in staging and plan a controlled maintenance window |

Production migrations run before the new API rollout. During a pre-upgrade migration, the previous API may still serve requests, so the migrated schema must remain compatible with it until replacement.

### Use expand and contract for breaking changes

1.  **Expand:** add the new schema while preserving the old schema.
2.  **Migrate:** backfill data and make the application support both representations.
3.  **Switch:** move reads and writes to the new representation.
4.  **Contract:** remove the old schema in a later release.

## Inspect migration state

Run direct Alembic commands from `api/` so the repository configuration and migration directory resolve correctly.

```
cd api
uv run python -m alembic current
```

```
cd api
uv run python -m alembic heads
```

```
cd api
uv run python -m alembic history --verbose
```

The repository expects exactly one head. Run the canonical check from its root:

```
make check-migrations
```

If the database is behind the repository head, determine why before generating another revision. Autogeneration against an outdated or incorrect database can produce misleading operations.

## Create a migration

Update the relevant SQLModel models first. When adding a model module, ensure the migration environment imports it before `SQLModel.metadata` is evaluated.

```
make makemigrations
```

Enter a short message when prompted, such as `add agent retention policy`. Alembic creates an autogenerated revision under `api/migrations/versions/` with a filename resembling `<revision>_add_agent_retention_policy.py`.

The revision defines `revision`, `down_revision`, `branch_labels`, `depends_on`, `upgrade()`, and `downgrade()`.

**Autogeneration requires review**

Autogeneration is a starting point, not approval. Alembic cannot infer every data migration, compatibility requirement, or safe production sequence.

## Review the migration

Read the entire generated revision before applying it. Confirm `down_revision` targets the intended current head, the graph remains continuous, and `make check-migrations` reports one head.

### Review generated operations

-   Unexpected table or column deletion, or a rename represented as drop and add
-   Nullability changes without a backfill and unique constraints existing data may violate
-   Foreign keys, indexes, and table rewrites that may conflict with rows or hold locks
-   Server defaults, PostgreSQL enums, and type changes that require an explicit `USING` expression
-   Missing application fields or operations caused by connecting to the wrong database

### Review data transformations

Make transformations deterministic and safe for the expected volume. Account for malformed values, intermediate uniqueness, stable ordering, foreign keys, lock duration, and transaction failure. A uniqueness migration may require a collision-free temporary state.

### Review the downgrade

Reverse the revision intentionally when reversal is safe. If a downgrade destroys data or cannot reconstruct the old representation, document that limit, test the real recovery strategy, require a backup, and prefer a forward corrective migration after production data changes.

**Do not rewrite shared history**

Once a revision has reached a shared environment, create a corrective migration instead of rewriting its history. Rewrite only a revision that has never left isolated development.

## Resolve multiple heads

Multiple heads usually appear when branches independently add revisions from the same parent. Inspect both branches before merging:

```
make check-migrations

cd api
uv run python -m alembic heads
uv run python -m alembic history --verbose
```

Confirm that both branches are compatible, especially when they touch the same table, constraint, column, enum, or data set. Then create the merge revision from the repository root:

```
make merge-heads
```

The command fails when no heads exist, does nothing with exactly one head, and creates a merge revision only for multiple heads. A merge revision normally has multiple `down_revision` values and empty upgrade and downgrade functions.

**A merge joins only the graph**

It does not reconcile conflicting schema operations or repair incompatible data transformations. Resolve those conflicts first, then run `make check-migrations` again.

## Test the migration

Test against PostgreSQL, not an approximation. The API suite starts a temporary PostgreSQL database and upgrades it to Alembic `head` before tests:

```
make check-api
make check-migrations
make test-api
```

API CI runs `make check-migrations`. The test path proves the complete migration chain can build a clean database.

### Test existing data

Use a staging-sized or sanitized realistic copy for backfills, constraints, enum or type changes, identifier rewrites, destructive operations, and work that may hold long locks. Verify both schema and affected rows.

### Test downgrade and reapplication

```
make rollback
make migrate
```

Confirm the prior revision, old-application compatibility, data preservation, successful reapplication, and the final upgraded result. `make rollback` runs `alembic downgrade -1`; do not use it casually on a shared database.

### Use an isolated database override

```
cd api
ALEMBIC_DB_URL='postgresql+psycopg2://USER:PASSWORD@HOST:5432/DATABASE' \
  uv run python -m alembic upgrade head
```

`ALEMBIC_DB_URL` overrides the normal connection for isolated migration tests. Use secret injection rather than placing production credentials in shell history.

## Apply migrations locally

### Full local stack

```
./run.sh
```

The script starts PostgreSQL and Redis, builds the API image, applies `alembic upgrade head`, and only then starts Product API, Ingest, the Domain Event worker, Communications, and UI, alongside local Kubernetes dependencies. Application services do not start when migration fails.

### Host-managed development

```
make dev-api
```

`make dev-api` starts Product API on `8000`, Ingest on `8001`, and Communications on `8002`.

```
make migrate

cd api
uv run python -m alembic current
```

Do not mix host-managed services and the full Docker application stack against the same development workflow unless you deliberately understand the process and database used by each command.

## Deploy to staging

Pass every schema-changing release through `agent-farm-staging`. Confirm it has independent application data and credentials, a current backup, one Alembic head, recorded start and target revisions, and an explicit recovery choice.

Use this process order: back up the application database; preserve encryption and signing keys; make the new image available; apply Alembic migrations; confirm the intended head; roll Product, Ingest, Communications, and worker processes to compatible code; confirm Communications starts; check Connection and Delivery data through authorized surfaces; then run a Connection-level message canary. A new Communications process must not run against an older incompatible schema.

The API chart creates an `agentbarn-api-migrate` pre-install/pre-upgrade Job. It uses the target API image and runs:

```
cd /app/api && alembic upgrade head
```

Communications uses the same application schema and API image tag as Product API; it does not run an independent migration job. The hook must complete before the new Communications workload is considered ready, and migration failure should stop the rollout rather than be ignored. After deployment, verify authorized Connection and Delivery reads, Communications startup, provider health, Tool Calls through Ingest, workers, Event Deliveries, Activity, logs, costs, migration duration, and observed locking.

A database migration does not start or stop Agents, rebuild Runtime pods, reconnect provider sessions, reinstall provider applications, rewrite Platform policies, or rotate provider credentials. Once the new Communications process starts, it reconciles enabled Connection sessions from the migrated database state.

**Helm success is not verification**

Do not promote a release merely because the Helm command succeeded. Exercise the affected behavior and the selected recovery procedure in staging.

## Deploy to production

Confirm the exact commit and API image, one Alembic head, representative staging results, backward compatibility with the old API, a verified application database backup, the matching `AGENT_TOKEN_ENCRYPTION_KEY`, and named verification and recovery owners. Prevent overlapping deployments and communicate any maintenance window.

**Migration runs before API rollout**

A destructive migration can break the currently running API even if Helm never deploys the replacement image. Do not run a second manual upgrade while the Helm migration Job is active.

The migration Job has `backoffLimit: 2`. Successful hook Jobs are deleted; failed Jobs remain for diagnosis.

```
kubectl get job agentbarn-api-migrate \
  --namespace agent-farm

kubectl describe job agentbarn-api-migrate \
  --namespace agent-farm

kubectl logs job/agentbarn-api-migrate \
  --namespace agent-farm
```

```
kubectl get pods \
  --namespace agent-farm \
  --selector app.kubernetes.io/component=migration-job
```

Do not repeatedly redeploy until you know whether the failed attempt committed schema or data changes.

## Protect encrypted Communications data

Legacy provider-configuration backfill requires `AGENT_TOKEN_ENCRYPTION_KEY` when encrypted provider configuration exists. The migration uses the existing key to decrypt legacy provider credentials, re-encrypt them into Connection credential envelopes, and generate encrypted Connection driver credentials.

The same key must be available to the migration process, Product API, Communications, and every replica reading the resulting data. If it is missing or incorrect, migration should fail rather than silently create unusable Connections.

`COMMUNICATION_JOURNAL_RETENTION_DAYS=31` is the default content-free journal retention. Valid values range from `1` through `3650`. The Communications supervisor prunes journal entries; pruning is not an Alembic migration, changing retention needs no schema migration, and pruning does not delete canonical Conversation Messages. Do not confuse absent old journal rows with a failed schema migration.

-   Preserve the application-database backup, `AGENT_TOKEN_ENCRYPTION_KEY`, and signing key.
-   Record current deployed image identifiers, Alembic revision, and environment-specific Secret configuration.
-   Keep real Secret values out of command output and migration logs.

Restoring only the database without the matching encryption key is not usable credential recovery.

### Replica safety

Only one migration runner applies schema changes. Communications replicas must not execute Alembic on startup; PostgreSQL ingress leases coordinate provider sessions, not migration ownership. Old and new processes overlap only when migration compatibility is intentional, and the Helm migration hook remains the canonical production runner.

## Verify the migration

Check the database revision from the deployed production API environment:

```
kubectl exec \
  --namespace agent-farm \
  deployment/agentbarn-api \
  -- sh -c 'cd /app/api && alembic current'
```

Confirm it matches the release head, then check the public API using the actual production hostname:

```
curl --fail https://api.agentbarn.example.com/api/v1/health
```

Verify one Alembic head, Communication Connection tables, Agent and Organization ownership on active Connections, encrypted non-readable credentials, non-null Connection IDs on canonical Conversation Messages, expected Connection references on Delivery and journal rows, Product API Connection listing, Communications access to enabled Connections, provider health after rollout, Connection-aware Conversation reads, and Tool Call telemetry through Ingest separately. Prefer authorized Product API and UI checks over printing encrypted or decrypted credential columns.

### Connection canary

1.  Open an existing migrated Agent and confirm its expected Communication Connection, settings, and external identity where available.
2.  Confirm the credential value is not returned; enable the Connection if appropriate and start the Agent if stopped.
3.  Send one provider message that passes policy and confirm it appears under the expected Connection.
4.  Confirm inbound and outbound Delivery lifecycle completion, then confirm Tool Calls still appear through Ingest.

**Important**

Product API health alone does not validate a Communications data migration.

Record the starting and target revisions, deployment commit and image, start and finish time, backup identifier, verification results, and corrective actions.

## Rollback and recovery

| Situation | Preferred response |
| --- | --- |
| Migration did not start | Fix deployment configuration and retry |
| Migration failed and transaction rolled back cleanly | Diagnose the error, correct the migration, and redeploy |
| Migration succeeded but new API failed | Keep or restore an application version compatible with the migrated schema |
| Migration introduced a correctable schema problem | Create and deploy a forward corrective migration |
| Tested downgrade is safe and data-preserving | Run the controlled downgrade procedure |
| Migration destroyed or irreversibly transformed data | Stop writes and restore the verified recovery set |
| Database state is uncertain | Pause deployment and inspect revision, schema, data, and migration logs |

### Application rollback

A Helm rollback changes Kubernetes manifests and images. It does not run `alembic downgrade`. Confirm the older image can use the migrated schema before restoring it.

### Alembic downgrade

```
make rollback
```

This downgrades one revision using the locally configured database; it is not a complete deployment rollback. Before downgrading, review whether the revision drops Communications data, recreates legacy tables, loses fields without a legacy representation, deletes Conversation rows, conflicts with running code, needs the historical encryption key, or can preserve newly created Connections. Application rollback and database downgrade are separate decisions. Do not downgrade across the headless-Agent or Connection transition without a tested recovery plan.

### Database restoration

1.  Stop or isolate writers.
2.  Preserve logs and the failed database for investigation.
3.  Restore the application database backup.
4.  Restore the matching stable encryption key when rebuilding the environment.
5.  Deploy an image compatible with the restored revision.
6.  Verify the Alembic revision and run application and data-integrity checks.
7.  Reopen traffic only after verification succeeds.

**Recovery is operator-managed**

Agent Barn’s Helm charts do not automatically back up or restore database contents. Changing a Helm value or rolling back a release does not restore the database.

## Command reference

Run Make targets from the repository root.

| Command | Purpose |
| --- | --- |
| `make makemigrations` | Prompt for a message and generate an autogenerated revision |
| `make migrate` | Upgrade the configured database to head |
| `make rollback` | Downgrade the configured database by one revision |
| `make merge-heads` | Create a merge revision when multiple heads exist |
| `make check-migrations` | Fail unless the repository has exactly one Alembic head |
| `make test-api` | Run API tests against PostgreSQL migrated to head |
| `./run.sh` | Start the local stack and migrate before starting application services |

Run direct inspection commands from `api/`.

| Command | Purpose |
| --- | --- |
| `uv run python -m alembic current` | Show the revision applied to the configured database |
| `uv run python -m alembic heads` | Show the repository’s current migration heads |
| `uv run python -m alembic history --verbose` | Show the revision graph and migration history |
| `uv run python -m alembic upgrade head` | Apply all pending migrations |
| `uv run python -m alembic downgrade -1` | Reverse one revision |

### Operational safety checklist

#### Before production

-   One Alembic head and a manually reviewed revision
-   Upgrade testing against representative PostgreSQL data
-   Tested downgrade or forward-recovery behavior
-   Old-API compatibility and verified staging deployment
-   A recoverable backup with matching stable keys
-   Recorded revisions, deployment controls, and available owners

#### After production

-   The database reports the expected revision
-   API and UI health checks pass
-   Changed data and behavior are verified
-   Workers and reconciliation are healthy
-   Logs contain no unexplained schema errors
-   The release record contains backup and verification results

## Troubleshooting

| Symptom | Likely cause | Resolution |
| --- | --- | --- |
| make check-migrations reports multiple heads | Parallel branches created migrations from the same parent | Review both branches for conflicts, then run make merge-heads |
| Autogeneration wants to drop unrelated tables | Alembic loaded incomplete model metadata or connected to the wrong database | Verify model imports and DB\_CONNECTION\_URL before keeping the revision |
| Autogeneration creates no operations | The model was not imported or the database already matches it | Check api/migrations/env.py, model registration, and the target database |
| Migration cannot connect locally | DB\_CONNECTION\_URL is missing or not reachable from the host | Correct the repository-root .env or run through the Docker workflow |
| Migration fails on existing rows | A new constraint or type conflicts with production-shaped data | Add a validated backfill or split the change into expand and contract revisions |
| A unique backfill collides | Intermediate values violate an existing unique constraint | Use a deterministic collision-free intermediate state |
| Kubernetes upgrade stops at the migration hook | The Alembic Job failed before application rollout | Inspect the retained Job and pod logs before retrying |
| No successful migration Job exists to inspect | Successful hook Jobs are deleted | Verify alembic current from the running API pod |
| Helm rollback leaves the application broken | The database remained at the newer revision | Deploy a compatible image, run a tested downgrade, or restore the database |
| Old API fails during the pre-upgrade hook | The migration was not backward-compatible | Restore service, then redesign the change using expand and contract |
| Migration is much slower in production | Staging data volume or lock behavior was not representative | Stop repeated attempts, assess locks, and schedule a controlled migration |
| make rollback affects the wrong environment | The configured connection targeted another database | Stop, preserve evidence, assess changes, and recover from the appropriate backup |
| LiteLLM or Firecrawl schema is unchanged | Agent Barn Alembic does not manage those databases | Follow the migration process supplied by the owning upstream service |

## Next steps

Continue with [Self-hosting Communications](/guides/self-hosting/communications), [self-hosting configuration](/guides/self-hosting/configuration), [Deploy Kubernetes](/guides/self-hosting/deploy-kubernetes), and [Communication diagnostics](/guides/observe-and-govern/communication-diagnostics) for the operational checks around a migrated release.

[Continue operating the platform **Monitor the platform →** Configure signals for database, API, worker, Agent, and provider failures.](/guides/self-hosting/monitoring)
