This guide is the recommended path to deploy libsqlite in production on Ubuntu, VPS, or Coolify.
It assumes:
- you have a domain or subdomain ready
- you can mount a persistent volume
- you want the backend and frontend to be deployable as separate Docker apps
Recommended split:
backend: API, authentication, RBAC, control plane, migrations, discovery, SQLite/libsql operationsfrontend: admin panel UI
Recommended storage split:
- control plane DB:
control.db - managed SQLite databases: structured tree under
SQLITE_STORAGE_ROOT - optional raw discovery path: mounted folder with existing
.dbfiles before adoption
Recommended routing:
panel.tudominio.com-> frontendapi.tudominio.com-> backend- optional
*.tudominio.com-> per-database hostnames if you later wire wildcard routing
Create a new Docker app for the backend.
Suggested settings:
- build context:
backend/ - Dockerfile:
backend/Dockerfile - exposed port:
3000 - health check:
/health
Use values like these:
MASTER_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
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-discovery
SQLITE_DISCOVERY_PROJECT_ID=<project-id>
SQLITE_DISCOVERY_ADOPT=true
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_PATHshould point to a mounted directory with existing.dbfiles.SQLITE_DISCOVERY_ADOPT=truemakes the backend copy discovered files into managed storage.ALLOW_PUBLIC_REGISTRATION=falseis strongly recommended for public deployments after the initial admin bootstrap.SQLITE_PERFORMANCE_PROFILE=performanceprioritizes throughput and lower latency for ERP-style workloads.DB_CONNECTION_POOL_MAX_SIZEandDB_CONNECTION_POOL_IDLE_TTL_MStune backend client reuse.LIBSQL_RUNTIME_*resource variables let you constrain managed runtime containers when needed.
Mount:
/app/data
Recommended host layout:
/srv/libsqlite/backend-data/
control.db
sqlite/
projects/
<projectId>/
databases/
<databaseId>.db
sqlite-discovery/
legacy-app.db
Create a second Docker app for the frontend.
Suggested settings:
- build context:
frontend/ - Dockerfile:
frontend/Dockerfile - exposed port:
3000
NEXT_PUBLIC_API_URL=https://api.tudominio.com/api/v1The frontend only needs the public API URL.
Set DNS records:
api.tudominio.com-> backend servicepanel.tudominio.com-> frontend service
If you want wildcard hostnames later:
*.tudominio.com-> reverse proxy or Coolify wildcard routing
- Create the backend app.
- Set environment variables.
- Mount persistent storage.
- Deploy.
- Verify:
GET https://api.tudominio.com/health
GET https://api.tudominio.com/ready
Use the backend auth endpoint:
curl -X POST https://api.tudominio.com/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"admin@tudominio.com","password":"StrongPassword123!"}'- Create the frontend app.
- Set
NEXT_PUBLIC_API_URL. - Deploy.
- Open
https://panel.tudominio.com.
If you already have .db files on the server:
- Mount the discovery directory.
- Set
SQLITE_DISCOVERY_PATH. - Keep
SQLITE_DISCOVERY_ADOPT=false. - Scan the directory.
- Mount the directory.
- Set
SQLITE_DISCOVERY_ADOPT=true. - Let the backend adopt the files into managed storage.
Typical flow:
- Create a project.
- Create a new SQLite database.
- Save the generated database metadata.
- Use the returned file and subdomain identifier.
- Apply migrations from your code or CI.
If you have a file from another app or environment:
- Place the file in a readable path inside the backend container or VM.
- Call the import endpoint.
- The backend copies the file into managed storage.
- Apply migrations or queries as needed.
For remote libsql:
- Register the URL and token.
- Test the connection.
- Use the schema and query endpoints.
- Run migrations through the API.
Backup these items separately:
control.db- managed SQLite files under
SQLITE_STORAGE_ROOT - any raw discovery folder if you still keep one
Suggested backup cadence:
- control plane: daily
- managed databases: according to business criticality
- offsite copy: at least one additional location
- Restore the control plane database.
- Restore the managed SQLite tree.
- Verify file permissions.
- Verify
/healthand/ready. - Log in and inspect projects/databases.
Before accepting traffic:
- backend responds to
/health - backend responds to
/ready - frontend can reach
NEXT_PUBLIC_API_URL - audit log writes succeed
- storage volume is persistent
MASTER_KEYis correct
- forgetting to mount
/app/data - pointing
NEXT_PUBLIC_API_URLat localhost in production - using the wrong
MASTER_KEYacross restarts - enabling discovery without mounting the discovery path
- expecting the backend to scan arbitrary remote hosts automatically
- keep backend and frontend as separate services
- keep the control plane DB private
- keep managed SQLite files on persistent storage
- use migrations from code, not manual edits
- rotate tokens when users or environments change
- keep audit logs enabled