SKILL.md
Database Management Guide
Docklift uses SQLite as its database, managed by Prisma ORM.
Schema Location
backend/prisma/schema.prisma
Database file: data/docklift.db on the host → /app/data/docklift.db in the backend container (DATABASE_URL=file:/app/data/docklift.db).
Prisma client is a Proxy singleton (lib/prisma.ts) with reconnectPrisma() after restore replaces the SQLite file. Backups use VACUUM INTO snapshots — see system_administration skill.
Migration Model: checked-in Prisma migrations
Production boot runs node dist/scripts/ensureDb.js (see backend/Dockerfile CMD):
- Dedupe
envvariablesduplicates (so unique(projectid, service_name, key)can apply) prisma migrate deployagainstbackend/prisma/migrations/- Legacy installs that used
db push(noprismamigrationshistory) are baselined then
repaired idempotently (publishhostport, issecret, scoped env unique). After servicename exists, repair drops legacy envvariablesprojectidkeykey and ensures envvariablesprojectidservicenamekeykey — never recreates (project_id, key).
Never ship prisma db push --accept-data-loss on container startup. Local db:push is for dev experiments only.
Schema change workflow
- Edit
backend/prisma/schema.prisma. bunx prisma migrate dev --name <desc>(creates SQL underprisma/migrations/).bun run db:generate— typed client.- Type-check:
cd backend; bunx tsc --noEmit. - Verify on a copy of a real DB with
bun run db:ensure/ container boot — not onlydb push.
Core Commands
Run from the backend/ directory:
bun run db:studio # web GUI to browse/edit data
bun run db:generate # regenerate the typed client after editing the schema
bun run db:migrate # prisma migrate deploy
bun run db:ensure # production-equivalent bootstrap (dedupe + migrate + repair)
bun run db:push # local-only; do not use as the container boot path
Models
| Model | Table | Purpose |
|---|---|---|
User |
users |
Admin accounts. passwordChangedAt → JWT pwdv claim |
Project |
projects |
An application: source, build settings, status |
Service |
services |
One deployable unit within a project (Dockerfile, domain, ports) |
Deployment |
deployments |
Build/deploy history, trigger, captured logs |
EnvVariable |
env_variables |
Shared or per-service vars: servicename ("" = all services), isbuildarg / isruntime / issecret; @@unique([projectid, service_name, key]) |
PersistentVolume |
persistent_volumes |
Configured named-volume mounts per service |
Port |
ports |
Host port pool (islocked); used only when project publishhost_port is true |
Settings |
settings |
Key/value system settings (GitHub App creds, ACME email, panel domain) |
Build & storage fields on Project
| Field | Default | Meaning | ||
|---|---|---|---|---|
build_type |
"auto" |
auto \ |
dockerfile \ |
railpack |
base_directory |
"." |
Subdirectory to build from (monorepos) | ||
dockerfile_path |
null |
Explicit Dockerfile when not auto-detecting | ||
internal_port |
3000 |
Port the app listens on inside the container | ||
publishhostport |
false |
When true, publish host ports from the pool |
EnvVariable: dedupe via lib/envVariables.dedupeEnvVariables() inside scripts/ensureDb.ts before migrate deploy. Invalid keys → 400; duplicates → 409.
PersistentVolume has unique constraints on (projectid, name) and (projectid, servicename, mountpath), so one service cannot mount two volumes at the same path.
All child models cascade on project delete (onDelete: Cascade) — deleting a project removes its services, deployments, env vars, volume records and frees its ports.
Troubleshooting
- Client out of sync with schema →
bun run db:generate. - Startup fails on migrate → check
docker logs docklift-backendfor[ensureDb]; never “fix” with
--accept-data-loss in the Dockerfile.
- Fresh install has no tables →
ensureDb/ migrate deploy failed. - Restored backup looks stale → restore also reconciles projects and reloads nginx; see the
system_administration skill.