This project is designed to run as a Docker app in Coolify. The backend manages SQLite files and libsql connections, and it can also discover or adopt .db files mounted from the server.
For the full end-to-end setup, including Cloudflare DNS, wildcard subdomains, and API integration examples, see docs/PUBLIC_DATABASES.md. The panel also manages public database routing directly, so the Coolify env vars act as bootstrap defaults and fallback values..
backendservice: API + control plane.- Optional
frontendservice later: admin panel. - Persistent volume for SQLite control data and managed database files.
- Wildcard subdomain through your reverse proxy or Coolify routing.
- If you want to replace Turso completely, publish the managed libSQL runtime through a public hostname that you configure in Coolify, such as
db.example.comor*.db.example.com. - Keep real domains and tokens out of the repository; set them only in Coolify environment variables or the panel.
Create a new Docker application in Coolify using the backend repository.
Use these settings:
- Build context: repository root or
backend/depending on how you wire the app in Coolify. - Dockerfile:
backend/Dockerfile - Exposed port:
3000 - Health check path:
/health - If you are using Cloudflare Tunnel, make sure the hostname you expose for database traffic is reachable from your ERP and from the machine where you run migrations.
Set these in Coolify:
MASTER_KEY=<64 hex chars>
DATABASE_FILE=/app/data/control.db
PORT=3000
CORS_ORIGIN=https://panel.tudominio.com
SQLITE_STORAGE_ROOT=/app/data/sqlite
SQLITE_DISCOVERY_PATH=/app/data/sqlite
SQLITE_DISCOVERY_PROJECT_ID=<project-id>
SQLITE_DISCOVERY_ADOPT=true
DATABASE_PUBLIC_DOMAIN=db.example.com
DATABASE_PUBLIC_URL_TEMPLATE=
DATABASE_PUBLIC_BASE_URL=
DATABASE_PUBLIC_HOST=db.example.com
ALLOW_PUBLIC_REGISTRATION=false
SQLITE_PERFORMANCE_PROFILE=performance
DB_CONNECTION_POOL_MAX_SIZE=256
DB_CONNECTION_POOL_IDLE_TTL_MS=1800000
LIBSQL_RUNTIME_TOKEN_TTL_SECONDS=2592000
LIBSQL_RUNTIME_MEMORY_BYTES=0
LIBSQL_RUNTIME_CPU_NANO=0
LIBSQL_RUNTIME_PIDS_LIMIT=0Notes:
MASTER_KEYmust be 64 hex characters.SQLITE_STORAGE_ROOTis where managed SQLite files are written.SQLITE_DISCOVERY_PATHis the directory the backend scans for existing.dbfiles.SQLITE_DISCOVERY_ADOPT=truecopies discovered databases into the managed storage tree so everything stays unified.DATABASE_PUBLIC_DOMAINis the simplest option for wildcard subdomains such asdb.example.com, which becomeshttps://<subdomain>.db.example.com.DATABASE_PUBLIC_URL_TEMPLATEis still available if you need a custom URL pattern.DATABASE_PUBLIC_BASE_URLis optional if you prefer path-based URLs.DATABASE_PUBLIC_HOSTmust resolve to the same host your ERP or editor will use when opening the database connection.ALLOW_PUBLIC_REGISTRATION=falseis recommended after the first admin is created.SQLITE_PERFORMANCE_PROFILE=performanceis the fastest starting point for local self-hosted ERP-style workloads.DB_CONNECTION_POOL_*controls backend connection reuse and eviction.LIBSQL_RUNTIME_*lets you apply runtime CPU, memory and PID limits per managed database.- The panel should remain the source of truth for URL, token, and runtime details; the repository should only ship placeholders.
Mount persistent storage for:
/app/data
Recommended host structure on Ubuntu:
/srv/libsqlite/
control.db
sqlite/
projects/
<projectId>/
databases/
<databaseId>.db
If you already have existing .db files on the server, mount them into a separate read/write folder first, then use discovery or adoption.
You have two safe modes:
- Mount a directory with existing
.dbfiles. - Set
SQLITE_DISCOVERY_PATH. - Call the discovery endpoint or let startup scan it.
- The system registers the databases but leaves the physical files where they are.
- Mount a directory with existing
.dbfiles. - Set
SQLITE_DISCOVERY_PATHandSQLITE_DISCOVERY_ADOPT=true. - The backend copies the files into the managed storage tree:
data/sqlite/projects/<projectId>/databases/<databaseId>.db - After adoption, all managed files stay under one consistent hierarchy.
The backend generates a subdomain identifier for each database record.
Use your reverse proxy or Coolify routing to map:
panel.tudominio.com-> backend/admin panelapi.tudominio.com-> backend API*.tudominio.com-> optional per-database routing if you want database-specific hostnames
For a Turso-like experience, dedicate a separate database hostname such as db.example.com and route the per-database URLs there.
The database subdomain is metadata in the control plane; routing is handled by Coolify or your proxy layer.
In the create/import dialogs, leave the subdomain field blank to let the backend auto-generate a public subdomain.
To apply migrations without entering the panel:
- Register or import the database.
- Use the database URL and token from the control plane.
- Call:
POST /api/v1/databases/:id/migrations
This works for:
- local SQLite files managed by the backend
- libsql remote databases registered with URL + token
- Deploy backend in Coolify.
- Attach a persistent volume.
- Configure
MASTER_KEYand the storage/discovery env vars. - Create a project in the panel.
- Create a new SQLite database or import/adopt existing
.dbfiles. - Register libsql remote databases with URL + token.
- Run migrations from your code or CI pipeline.
- Copy the public URL and token from the database detail page and use them in your ERP or editor env vars.
Example app env:
DATABASE_URL=https://mi-db.db.example.com
DATABASE_AUTH_TOKEN=xxxxx- Docker deployment with restart policies.
- Environment variable management.
- Persistent storage.
- Reverse proxy and TLS.
- Optional wildcard subdomain routing.
The backend can only manage databases it can access or that you explicitly register/import. It does not safely scan arbitrary remote machines for credentials or database locations.