# Payload CMS con Turso paso a paso

> Cómo conectar Payload CMS a Turso: crear la base, configurar el adaptador SQLite, trabajar con un archivo en local y correr las migraciones en producción.

URL: https://codegea.com/payload-cms-con-turso-paso-a-paso
CMS · 2026-10-18 · Derian Pinto

Payload no tiene un adaptador «de Turso». Turso es SQLite en un servidor, y Payload ya sabe hablar con SQLite, así que se conecta con el mismo adaptador que usarías con un archivo local. Esto es lo que hacemos en este sitio, sin saltos.

Antes de entrar, conviene saber qué es Turso y cuándo tiene sentido. Lo dejamos corto: es una base de datos basada en libSQL (una bifurcación de SQLite) a la que te conectas por red con una URL y un token. Si lo que quieres es comparar con Postgres o MongoDB, mira [qué bases de datos soporta Payload CMS](/payload-cms-que-bases-de-datos-soporta).

## Paso 1: crea la base en Turso

Con la CLI de Turso instalada (está en su documentación):

```bash
turso auth login
turso db create mi-sitio
turso db show mi-sitio --url
turso db tokens create mi-sitio
```

El tercer comando te da una URL que empieza por `libsql://`. El cuarto, un token. Guarda las dos cosas. Si quieres que caduque, `turso db tokens create` acepta `--expiration`, y `--read-only` crea uno de solo lectura, útil para una réplica o un script de consulta.

## Paso 2: instala el adaptador

```bash
pnpm add @payloadcms/db-sqlite
```

## Paso 3: configura Payload

En `payload.config.ts`:

```ts
import { sqliteAdapter } from '@payloadcms/db-sqlite';

export default buildConfig({
  // …
  db: sqliteAdapter({
    client: {
      url: process.env.DATABASE_URL ?? 'file:./data/payload.db',
      authToken: process.env.DATABASE_AUTH_TOKEN,
    },
  }),
});
```

Y en `.env`:

```bash
# Local
DATABASE_URL=file:./data/payload.db

# Producción
# DATABASE_URL=libsql://mi-sitio-tu-usuario.turso.io
# DATABASE_AUTH_TOKEN=…
```

El mismo código sirve para los dos entornos. En local, `file:` abre un archivo SQLite y no hace falta token. En producción, `libsql://` más el token apuntan a Turso. No hay `if` en el código, solo cambian las variables.

## Paso 4: desarrollo con archivo, sin red

En desarrollo, el adaptador sincroniza el esquema solo cada vez que cambias una colección. Es cómodo: editas un campo y la tabla se actualiza. Verás una fila llamada `dev` en la tabla `payload_migrations`; es la marca de que la base se ha ido ajustando de esa forma, y es normal.

Trabajar contra un archivo local tiene otra ventaja: no gastas red ni cuota de Turso mientras desarrollas, y puedes borrar la base y sembrarla de nuevo en segundos.

## Paso 5: migraciones para producción

Ese ajuste automático **no** es lo que quieres en producción. Ahí se usan migraciones, que son archivos versionados en Git. Cuando termines un cambio de esquema:

```bash
pnpm payload migrate:create nombre-del-cambio
```

Genera un archivo en `src/migrations/` con las sentencias SQL. Lo revisas, lo subes con tu código y en el despliegue se aplica:

```bash
pnpm payload migrate
```

En nuestro `package.json` el script de build de Vercel hace las dos cosas seguidas:

```json
"vercel-build": "payload migrate && next build"
```

Así, cada despliegue aplica las migraciones pendientes contra Turso antes de compilar. Para que funcione, el entorno de build necesita `DATABASE_URL` y `DATABASE_AUTH_TOKEN`, no solo el de ejecución. Es el olvido más común.

## Lo que nos ha tocado vigilar

- **Un solo proceso a la vez sobre el archivo local.** Si tienes el servidor de desarrollo abierto y corres un script de seed sobre la misma base, puede fallar con `disk I/O error`. Usamos una base aparte para esas pruebas.
- **No mezcles ****`push`**** y migraciones en la misma base.** Si ya aplicaste cambios con el ajuste automático de desarrollo y luego creas la migración, la base local y el archivo pueden no coincidir. Ante la duda, recrea la base local desde cero.
- **Revisa el SQL de cada migración.** Sobre todo si renombras o eliminas columnas: SQLite maneja esos cambios con menos libertad que Postgres.

## Cuándo no usar Turso con Payload

SQLite admite **un escritor a la vez**. Para un sitio de contenido, donde se escribe poco y se lee mucho, no es un problema. Para una aplicación con muchas escrituras concurrentes (pedidos, sesiones, eventos en tiempo real), Postgres es la opción más cómoda.

Si quieres ver cómo encaja todo esto en un sitio real, en [así está construido codegea.com](/asi-esta-construido-codegea-nextjs-payload-turso-r2) contamos el stack completo.
