Codegea

Development

Custom software developmentSystems, platforms and integrations built around your operationCorporate websitesProfessional, fast and built to inspire trustOnline storesCatalog, payments and shipping ready to sell 24/7WooCommerceFast WooCommerce stores with each country’s payments and invoicingLanding pagesA page focused on one goal: turning visitors into customersMobile appsCustom iOS and Android apps that put your business in your customers’ pockets

Integration and CMS

CMS integrationsWordPress, Strapi, Directus and Arc XP connected to your whole ecosystemArc XPSupport, implementation and integrations for the media platformMigrationsMigrate your site, platform or database without losing anything

Growth and infrastructure

AI automationTechnology does the repetitive work and your team focuses on what mattersOn-page SEOBe found and recommended by Google: technical and content optimizationServers & hostingFast, secure servers monitored 24/7
RetainerYour dev team, by the monthFrom 20h a month. Fixed fee.
An AI quoting tool to quote services in minutesProfessional quotes in minutes, with projects, sellers and payments tracked in one place.See the case

By type

EcommerceStores and catalogsCorporate sitesBrand and lead genPlatforms & appsCustom software

Explore

All projectsCases and resultsClientsWho trusts usRetainerMonthly dev team

Blog

All articlesGuides and opinionNewsletterOne email a monthContactWe reply in 24h

Latest articles

How codegea.com is built: Next.js, Payload, Turso and R2CMS · 2026-10-20Cloudflare Workers: what they are and when to use themNode.js · 2026-10-19Live Preview in Payload with Next.jsCMS · 2026-10-19
NewsletterJoin our newsletterPractical web, SEO and AI notes.
ServicesCustom software developmentCorporate websitesOnline storesWooCommerceLanding pagesMobile appsCMS integrationsArc XPMigrationsAI automationOn-page SEOServers & hostingYour dev team, by the month
ProjectsProfessional quotes in minutes, with projects, sellers and payments tracked in one place.EcommerceCorporate sitesPlatforms & appsAll projectsClientsRetainer
Retainer
BlogAll articlesNewsletterContactHow codegea.com is built: Next.js, Payload, Turso and R2Cloudflare Workers: what they are and when to use themLive Preview in Payload with Next.jsJoin our newsletter
ESEN
Let's talk

CMS

Payload CMS with Turso step by step

How to connect Payload CMS to Turso: create the database, configure the SQLite adapter, work with a local file and run migrations in production.

Derian Pinto · Founder & Full Stack Developer2026-10-183 min

Payload has no "Turso adapter". Turso is SQLite on a server, and Payload already knows how to talk to SQLite, so you connect with the same adapter you would use with a local file. This is what we do on this site, no detours.

First, a quick word on what Turso is and when it makes sense. It is a database based on libSQL (a fork of SQLite) that you reach over the network with a URL and a token. If you want to compare it with Postgres or MongoDB, see which databases Payload CMS supports.

Step 1: create the database in Turso

With the Turso CLI installed (it is in their documentation):

turso auth login
turso db create my-site
turso db show my-site --url
turso db tokens create my-site

The third command gives you a URL that starts with libsql://. The fourth gives you a token. Save both. If you want it to expire, turso db tokens create accepts --expiration, and --read-only creates a read-only one, useful for a replica or a query script.

Step 2: install the adapter

pnpm add @payloadcms/db-sqlite

Step 3: configure Payload

In payload.config.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,
    },
  }),
});

And in .env:

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

# Production
# DATABASE_URL=libsql://my-site-your-user.turso.io
# DATABASE_AUTH_TOKEN=…

The same code works for both environments. Locally, file: opens a SQLite file and no token is needed. In production, libsql:// plus the token point to Turso. There is no if in the code, only the variables change.

Step 4: develop with a file, no network

In development, the adapter syncs the schema on its own every time you change a collection. It is convenient: you edit a field and the table updates. You will see a row called dev in the payload_migrations table; it marks that the database has been adjusted this way, and it is normal.

Working against a local file has another advantage: you spend no network or Turso quota while developing, and you can delete the database and seed it again in seconds.

Step 5: migrations for production

That automatic adjustment is not what you want in production. There you use migrations, which are versioned files in Git. When you finish a schema change:

pnpm payload migrate:create change-name

It generates a file in src/migrations/ with the SQL statements. You review it, ship it with your code and on deploy it is applied:

pnpm payload migrate

In our package.json the Vercel build script does both, back to back:

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

So every deploy applies the pending migrations against Turso before building. For that to work, the build environment needs DATABASE_URL and DATABASE_AUTH_TOKEN, not just the runtime one. It is the most common oversight.

What we have had to watch out for

  • One process at a time on the local file. If the dev server is open and you run a seed script against the same database, it can fail with disk I/O error. We use a separate database for those tests.
  • Don't mix push and migrations on the same database. If you already applied changes with the automatic development adjustment and then create the migration, the local database and the file may not match. When in doubt, recreate the local database from scratch.
  • Read the SQL of every migration. Especially if you rename or drop columns: SQLite handles those changes with less freedom than Postgres.

When not to use Turso with Payload

SQLite allows one writer at a time. For a content site, where you write little and read a lot, that is not a problem. For an application with many concurrent writes (orders, sessions, real-time events), Postgres is the more comfortable choice.

To see how all this fits in a real site, how codegea.com is built covers the full stack.

Share this article?LinkedInWhatsAppX

Written by

Derian Pinto

Founder & Full Stack Developer

Founder of Codegea. Builds custom websites, stores and platforms, and integrates and supports Arc XP for media companies.

Join our newsletter

An email with news and practical web, SEO and AI notes. No spam, unsubscribe anytime.

Shall we build your project?

Need help? Chat with us