
Sealos Database
- 11 installs
- 1 repo stars
- Updated June 18, 2026
- zjy365/sealos-skills
Provisions, connects, and operates Sealos Cloud databases via sealos-cli and wires DATABASE_URL and other env vars into a dev environment.
About
Identifies an app's database need, creates or reuses a Sealos PostgreSQL/MySQL/MongoDB/Redis database, fetches connection details, and wires only the needed local env vars. A developer uses it to give a project a managed cloud database or replace local Docker Compose databases.
- Never prints passwords or full connection strings and won't commit secrets
- Parses sealos-cli JSON output and confirms before destructive or public-access ops
Sealos Database by the numbers
- 11 all-time installs (skills.sh)
- Ranked #651 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/zjy365/sealos-skills --skill sealos-databaseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11 |
|---|---|
| repo stars | ★ 1 |
| Last updated | June 18, 2026 |
| Repository | zjy365/sealos-skills ↗ |
What it does
Provisions, connects, and operates Sealos Cloud databases via sealos-cli and wires DATABASE_URL and other env vars into a dev environment.
Files
Sealos Database
Use this skill to give a project a real Sealos Cloud database during development. The default outcome is: identify the app's database need, create or reuse a Sealos database with sealos-cli, fetch connection details, wire only the needed local env vars, and verify the app can connect.
Safety Rules
1. Never print database passwords or full connection strings in the final answer. 2. Do not overwrite an existing env value without confirming or preserving the old value. 3. Do not commit .env, .env.local, connection strings, passwords, kubeconfig, or Sealos auth files. 4. Ask before enabling public database access. Prefer private connections when the app runs inside Sealos/Devbox. 5. Ask before destructive operations: database delete, backup-delete, restoring over a name that may collide, or disabling access that an active app depends on. 6. Use JSON output from sealos-cli by default and parse it instead of scraping table output.
Workflow
1. Resolve the target project
Confirm the working directory with pwd or git rev-parse --show-toplevel.
Run the analyzer when a project directory is available:
node <SKILL_DIR>/scripts/analyze-project-database.mjs <project-dir>Use the analyzer result as a starting point, then inspect the real files it cites before editing anything. It intentionally avoids printing secret values.
2. Check sealos-cli
Prefer an existing sealos-cli binary:
sealos-cli --version
sealos-cli database --help
sealos-cli whoamiIf it is not installed, use npx -y sealos-cli@latest ... for one-off commands. Ask before installing it globally.
If auth is missing or expired, run:
sealos-cli login <region>
sealos-cli workspace list
sealos-cli workspace currentUse the workspace the user expects. If multiple workspaces exist and the target is ambiguous, ask before provisioning.
3. Choose create or reuse
List existing databases first:
sealos-cli database list -o jsonReuse an existing database when the name, type, and purpose match. Create a new one when the project has no suitable database or the user asks for a fresh dev database.
Use conservative development defaults unless the project clearly needs more:
sealos-cli database create postgresql --name <app-dev-db> --cpu 1 --memory 1 --storage 3 --replicas 1 -o jsonBefore creating, check supported versions if version choice matters:
sealos-cli database versions --type postgresql -o jsonSupported CLI database types include postgresql, mongodb, mysql, apecloud-mysql, redis, kafka, qdrant, nebula, weaviate, milvus, pulsar, and clickhouse. Use the type detected from the project; default to postgresql only when the project has no database-specific signals.
4. Wait for readiness and fetch connection data
Poll details until the database is running or connection data is present:
sealos-cli database get <name> -o json
sealos-cli database connection <name> -o jsonRead references/sealos-cli-database.md for the current command contract and response handling.
5. Wire the development environment
Map the connection into the env var the project already uses:
| Project signal | Preferred env key |
|---|---|
| Prisma, Drizzle, TypeORM, generic Postgres | DATABASE_URL |
| MySQL app with existing MySQL-specific config | DATABASE_URL or existing MYSQL_URL |
| MongoDB app | MONGODB_URI |
| Redis cache/queue | REDIS_URL |
Use the existing local env convention:
1. Prefer .env.local for Next.js and frontend-adjacent projects. 2. Prefer .env only when the repo already uses it for local development and it is gitignored. 3. Treat .env.example as documentation only; never write real secrets there. 4. Preserve comments and unrelated keys.
If a connection string is not directly returned in the desired form, compose it from host, port, username, and password fields from sealos-cli database connection.
6. Verify application connectivity
Run the project's normal verification path, not just the CLI command:
1. Run migrations or introspection if the project has a clear command (prisma migrate, drizzle-kit migrate, db:migrate, db:push). 2. Start the app or run the smallest test that opens a DB connection. 3. If the app runs outside Sealos and cannot reach the private endpoint, ask before enabling public access:
sealos-cli database enable-public <name> -o json
sealos-cli database connection <name> -o jsonDisable public access after testing if it is no longer needed:
sealos-cli database disable-public <name> -o json7. Report the result
Summarize:
1. Database name, type, region/workspace, and status. 2. Env file and key updated, without revealing the secret value. 3. Verification command and outcome. 4. Any public access state and follow-up action.
Common Tasks
Connect an existing project to a Sealos database
1. Run the analyzer. 2. Inspect the env/config files it cites. 3. List existing Sealos databases. 4. Create or reuse the matching database. 5. Fetch connection details. 6. Write the expected env key. 7. Run the app's DB verification.
Replace a local Compose database for development
1. Identify the app service env vars that point at postgres, mysql, mongo, or redis compose services. 2. Provision the equivalent Sealos database. 3. Update only the app's local env file, not the compose file, unless the user asks to remove the local service. 4. Keep local Compose rollback simple: the original compose service remains available.
Add a database to a Devbox workflow
1. Use private database connection details when the Devbox runs in the same Sealos workspace. 2. Write env vars into the Devbox/app environment expected by the repo. 3. Restart or reload the Devbox process only after env vars are in place.
References
scripts/analyze-project-database.mjs- read-only project database intent analyzer.references/sealos-cli-database.md-sealos-cli databasecommand contract.references/env-integration.md- safe env-file editing and connection-string mapping.
interface:
display_name: "Sealos: Sealos Database"
short_description: "Use Sealos cloud databases while developing."
default_prompt: "Use $sealos-database to create or connect a Sealos Cloud database for this project."
{
"skill_name": "sealos-database",
"evals": [
{
"id": 0,
"prompt": "/sealos-database create a cloud Postgres database for this repo and wire DATABASE_URL",
"expected_output": "Analyzes the project, checks sealos-cli auth/workspace, lists existing databases, creates or reuses PostgreSQL, writes the local DATABASE_URL without printing secrets, and verifies the app database path.",
"files": [],
"assertions": [
{
"name": "uses-analyzer",
"description": "Runs scripts/analyze-project-database.mjs or performs equivalent file-backed detection before choosing the database type"
},
{
"name": "uses-sealos-cli",
"description": "Uses sealos-cli database commands instead of raw kubectl database manifests"
},
{
"name": "protects-secrets",
"description": "Does not print passwords or full connection strings in the final answer"
}
]
},
{
"id": 1,
"prompt": "/sealos-database connect this app to an existing Sealos Redis database for local development",
"expected_output": "Lists existing databases, selects a matching Redis database or asks if ambiguous, fetches connection details, updates the existing Redis env key, and verifies the app's cache/queue path.",
"files": [],
"assertions": [
{
"name": "prefers-reuse",
"description": "Checks database list before creating a new database"
},
{
"name": "maps-env-key",
"description": "Uses REDIS_URL or the existing project-specific Redis env key"
}
]
},
{
"id": 2,
"prompt": "/sealos-database my local laptop cannot reach the private database endpoint",
"expected_output": "Explains that local-machine access may require public access, asks before enabling it, and recommends disabling public access after testing if no longer needed.",
"files": [],
"assertions": [
{
"name": "asks-before-public",
"description": "Does not run enable-public without user confirmation"
},
{
"name": "cleanup-guidance",
"description": "Mentions disable-public when public access is temporary"
}
]
}
]
}
Environment Integration
Use this reference when wiring Sealos database connection data into a development project.
File Choice
Prefer the project's existing convention:
1. .env.local for Next.js and similar local-only app config. 2. .env when the repo already uses it for local development and it is ignored by git. 3. Framework-specific files such as .dev.vars, .env.development, or apps/*/.env.local when the code already reads them. 4. .env.example only for placeholder documentation. Never write real secrets into example files.
Before writing secrets, verify the file is ignored:
git check-ignore .env .env.local .env.developmentIf the target file is tracked or not ignored, stop and choose an ignored local env file instead.
Env Key Mapping
Prefer keys already used by the app.
| Database | Common keys |
|---|---|
| PostgreSQL | DATABASE_URL, POSTGRES_URL, POSTGRES_PRISMA_URL |
| MySQL | DATABASE_URL, MYSQL_URL, MYSQL_DATABASE_URL |
| MongoDB | MONGODB_URI, MONGO_URL, DATABASE_URL |
| Redis | REDIS_URL, KV_URL, CACHE_URL, QUEUE_REDIS_URL |
| Qdrant | QDRANT_URL, QDRANT_API_KEY |
| Weaviate | WEAVIATE_URL, WEAVIATE_API_KEY |
| ClickHouse | CLICKHOUSE_URL |
If multiple keys exist, update the one read by the runtime entry point or ORM config. Do not create extra aliases unless the app needs them.
Connection String Shapes
Use a connection string returned by sealos-cli database connection when available. If only components are returned, compose the minimal expected form:
postgresql://<username>:<password>@<host>:<port>/<database>
mysql://<username>:<password>@<host>:<port>/<database>
mongodb://<username>:<password>@<host>:<port>/<database>?authSource=admin
redis://:<password>@<host>:<port>/0For PostgreSQL, default the database path to postgres unless the project explicitly expects another database name. If the app requires a non-default database, create it with the app's migration/bootstrap command or a safe one-time SQL command only after confirming the target.
Editing Rules
1. Preserve comments, blank lines, and unrelated keys. 2. Replace only the selected key. 3. If the key exists and has a non-empty value, preserve the old value in chat as "replaced existing local value" without printing it. 4. Quote values only if the project's env files already use quotes or the value contains characters that the loader requires quoted. 5. Never print the full resulting connection string in the final answer.
Verification
Use the project's own path:
- Prisma:
npx prisma db pull,npx prisma migrate status, or the repo's migration script. - Drizzle:
npx drizzle-kit check,npx drizzle-kit migrate, or the repo's migration script. - Rails:
bin/rails db:prepareorbin/rails db:migrate. - Django:
python manage.py migrate --checkorpython manage.py migrate. - Generic Node: run the app's smallest server/test script that opens a DB connection.
If the app runs from the user's laptop and private Sealos endpoints are unreachable, ask before enabling public access with sealos-cli database enable-public <name> -o json.
sealos-cli Database Reference
Use sealos-cli as the execution layer for Sealos Cloud database work. It is a Node.js Commander CLI whose database commands call dbprovider.<region>/api/v2alpha with auth from ~/.sealos/kubeconfig.
Install and Auth
Prefer an existing binary:
sealos-cli --version
sealos-cli whoamiUse one-off execution when the binary is missing:
npx -y sealos-cli@latest --versionAuthenticate and choose the workspace:
sealos-cli login https://usw-1.sealos.io
sealos-cli workspace list
sealos-cli workspace switch <workspace-id-or-team-name>
sealos-cli workspace currentsealos-cli stores auth metadata at ~/.sealos/auth.json and the active workspace kubeconfig at ~/.sealos/kubeconfig. Do not print or commit these files.
Provider Host Resolution
Database commands use this precedence:
1. SEALOS_DATABASE_HOST if set. 2. SEALOS_REGION if set, rewritten to dbprovider.<region-host>. 3. Region saved by sealos-cli login, rewritten to dbprovider.<region-host>. 4. Default region.
Read Commands
Use JSON for automation:
sealos-cli database list -o json
sealos-cli database versions -o json
sealos-cli database versions --type postgresql -o json
sealos-cli database get <name> -o json
sealos-cli database connection <name> -o json
sealos-cli database backups <name> -o jsondatabase connection may return private and public connection fields. Prefer private details for apps running inside Sealos/Devbox. Public access may be disabled by default.
Create and Update
Create with conservative development resources unless project needs say otherwise:
sealos-cli database create postgresql --name <name> --cpu 1 --memory 1 --storage 3 --replicas 1 -o jsonSupported create types:
postgresqlmongodbmysqlapecloud-mysqlrediskafkaqdrantnebulaweaviatemilvuspulsarclickhouse
Useful options:
--version <version>
--cpu <cpu>
--memory <gb>
--storage <gb>
--replicas <count>
--termination-policy <delete|wipeout>
--backup-start
--backup-type <day|hour|week>
--backup-week <day>
--backup-hour <00-23>
--backup-minute <00-59>
--backup-save-time <count>
--backup-save-type <days|hours|weeks|months>
--param KEY=VALUEUpdate resources:
sealos-cli database update <name> --cpu 2 --memory 4 --storage 10 -o jsonOperations
Non-destructive lifecycle operations:
sealos-cli database start <name> -o json
sealos-cli database pause <name> -o json
sealos-cli database restart <name> -o jsonBackups:
sealos-cli database backup <name> --name <backup-name> -o json
sealos-cli database backups <name> -o json
sealos-cli database restore <name> --from <backup-name> --name <restored-name> -o jsonPublic access:
sealos-cli database enable-public <name> -o json
sealos-cli database disable-public <name> -o jsonAsk before enabling public access. Disable it after local-machine testing when it is no longer needed.
Destructive commands require explicit user confirmation:
sealos-cli database delete <name> -o json
sealos-cli database backup-delete <databaseName> <backupName> -o jsonLogs
Discover log files before reading logs:
sealos-cli database log-files <pod-name> --db-type postgresql --log-type runtimeLog -o json
sealos-cli database logs <pod-name> --db-type postgresql --log-type runtimeLog --log-path <path> -o jsonSupported log DB types are postgresql, mongodb, mysql, and redis. Supported log types are runtimeLog, slowQuery, and errorLog.
Response Handling
Expect JSON by default. Treat operation responses with status: "requested" as asynchronous. Poll database get or database connection until the database status and connection fields are ready.
Do not rely on table output for automation. Table output is only for human inspection with -o table.
#!/usr/bin/env node
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
import { basename, join, resolve } from 'node:path'
const root = resolve(process.argv[2] || process.cwd())
const MAX_FILE_BYTES = 1024 * 1024
const MAX_FINDINGS_PER_KIND = 40
const ignoredDirs = new Set([
'.git',
'node_modules',
'dist',
'build',
'.next',
'.turbo',
'.venv',
'venv',
'__pycache__',
'coverage',
'.sealos'
])
const textFilePatterns = [
/^package\.json$/,
/^pnpm-lock\.yaml$/,
/^package-lock\.json$/,
/^yarn\.lock$/,
/^bun\.lockb?$/,
/^docker-compose.*\.ya?ml$/,
/^compose.*\.ya?ml$/,
/^\.env.*$/,
/^.*\.env$/,
/^.*\.prisma$/,
/^drizzle\.config\.(ts|js|mjs|cjs)$/,
/^.*\.(ts|tsx|js|jsx|mjs|cjs|py|rb|go|rs|java|kt|php|yaml|yml|json|toml)$/
]
const signals = [
{
type: 'postgresql',
confidence: 4,
patterns: [
/\bpostgres(?:ql)?:\/\//i,
/\bPOSTGRES(?:QL)?_/i,
/\bprovider\s*=\s*["']postgres(?:ql)?["']/i,
/["']postgres(?:ql)?["']\s*:\s*\{/i,
/\bpg\b/,
/drizzle-orm\/node-postgres/i,
/postgresql/i,
/psycopg/i
]
},
{
type: 'mongodb',
confidence: 4,
patterns: [
/\bmongodb(?:\+srv)?:\/\//i,
/\bMONGODB_URI\b/i,
/\bMONGO_URL\b/i,
/\bmongoose\b/i,
/\bmongodb\b/i
]
},
{
type: 'mysql',
confidence: 3,
patterns: [
/\bmysql:\/\//i,
/\bMYSQL_/i,
/\bmysql2\b/i,
/\bprovider\s*=\s*["']mysql["']/i,
/["']mysql["']\s*:\s*\{/i,
/\bprisma.*mysql/i
]
},
{
type: 'redis',
confidence: 3,
patterns: [
/\bredis:\/\//i,
/\bREDIS_URL\b/i,
/\bioredis\b/i,
/@upstash\/redis/i,
/\bbullmq?\b/i
]
},
{
type: 'qdrant',
confidence: 2,
patterns: [/\bQDRANT_/i, /\bqdrant\b/i]
},
{
type: 'weaviate',
confidence: 2,
patterns: [/\bWEAVIATE_/i, /\bweaviate\b/i]
},
{
type: 'clickhouse',
confidence: 2,
patterns: [/\bCLICKHOUSE_/i, /\bclickhouse\b/i]
}
]
const envKeyByType = {
postgresql: ['DATABASE_URL', 'POSTGRES_URL', 'POSTGRES_PRISMA_URL'],
mysql: ['DATABASE_URL', 'MYSQL_URL', 'MYSQL_DATABASE_URL'],
mongodb: ['MONGODB_URI', 'MONGO_URL', 'DATABASE_URL'],
redis: ['REDIS_URL', 'KV_URL', 'CACHE_URL', 'QUEUE_REDIS_URL'],
qdrant: ['QDRANT_URL'],
weaviate: ['WEAVIATE_URL'],
clickhouse: ['CLICKHOUSE_URL']
}
function isTextCandidate (filePath) {
const name = basename(filePath)
return textFilePatterns.some((pattern) => pattern.test(name))
}
function walk (dir, files = []) {
let entries
try {
entries = readdirSync(dir, { withFileTypes: true })
} catch {
return files
}
for (const entry of entries) {
if (ignoredDirs.has(entry.name)) continue
const fullPath = join(dir, entry.name)
if (entry.isDirectory()) {
walk(fullPath, files)
continue
}
if (!entry.isFile()) continue
if (!isTextCandidate(fullPath)) continue
try {
if (statSync(fullPath).size > MAX_FILE_BYTES) continue
} catch {
continue
}
files.push(fullPath)
}
return files
}
function safeRead (filePath) {
try {
return readFileSync(filePath, 'utf8')
} catch {
return ''
}
}
function relative (filePath) {
return filePath.startsWith(root) ? filePath.slice(root.length + 1) : filePath
}
function addFinding (bucket, finding) {
if (bucket.length >= MAX_FINDINGS_PER_KIND) return
bucket.push(finding)
}
function fileWeight (filePath) {
const rel = relative(filePath)
const name = basename(filePath)
if (/^\.env|\.env$/.test(name) || name.includes('.env.')) return 5
if (name === 'package.json') return 4
if (/docker-compose.*\.ya?ml$|compose.*\.ya?ml$/i.test(name)) return 3
if (/schema\.prisma$|drizzle\.config\.(ts|js|mjs|cjs)$/.test(rel)) return 4
if (/(^|\/)(prisma|drizzle|migrations|db\/migrations|database\/migrations)(\/|$)/.test(rel)) return 3
if (/(^|\/)(__tests__|test|tests|spec|specs|coverage|docs?|generated)(\/|$)/i.test(rel)) return 0.2
if (/_openapi\.json$|openapi|swagger/i.test(rel)) return 0.1
if (/README|CHANGELOG|LICENSE|SECURITY/i.test(name)) return 0.2
return 1
}
function scanEnvFiles (files) {
const envFiles = []
const keys = {}
for (const filePath of files) {
const name = basename(filePath)
if (!/^\.env|\.env$/.test(name) && !name.includes('.env.')) continue
const content = safeRead(filePath)
const fileKeys = []
for (const [index, line] of content.split(/\r?\n/).entries()) {
const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/)
if (!match) continue
const key = match[1]
fileKeys.push(key)
if (!keys[key]) keys[key] = []
keys[key].push({ file: relative(filePath), line: index + 1 })
}
envFiles.push({ file: relative(filePath), keys: fileKeys })
}
return { envFiles, keys }
}
function scanPackageJson () {
const packagePath = join(root, 'package.json')
if (!existsSync(packagePath)) return null
try {
const pkg = JSON.parse(readFileSync(packagePath, 'utf8'))
return {
packageManager: pkg.packageManager || null,
scripts: pkg.scripts || {},
dependencies: {
...pkg.dependencies,
...pkg.devDependencies
}
}
} catch {
return null
}
}
function detectMigrations (files) {
const candidates = [
'prisma/schema.prisma',
'prisma/migrations',
'drizzle',
'migrations',
'db/migrations',
'database/migrations'
]
const found = []
for (const candidate of candidates) {
if (existsSync(join(root, candidate))) {
found.push(candidate)
}
}
for (const filePath of files) {
const rel = relative(filePath)
if (/drizzle\.config\.(ts|js|mjs|cjs)$/.test(rel) && !found.includes(rel)) found.push(rel)
if (/schema\.prisma$/.test(rel) && !found.includes(rel)) found.push(rel)
}
return found
}
function scoreFiles (files) {
const scores = {}
const findings = []
for (const filePath of files) {
const content = safeRead(filePath)
if (!content) continue
const weight = fileWeight(filePath)
const lines = content.split(/\r?\n/)
for (const [lineIndex, line] of lines.entries()) {
for (const signal of signals) {
for (const pattern of signal.patterns) {
if (!pattern.test(line)) continue
scores[signal.type] = (scores[signal.type] || 0) + (signal.confidence * weight)
addFinding(findings, {
type: signal.type,
file: relative(filePath),
line: lineIndex + 1,
weight,
match: pattern.source
})
break
}
}
}
}
return { scores, findings }
}
function choosePrimary (scores) {
const ranked = Object.entries(scores)
.sort((a, b) => b[1] - a[1])
.map(([type, score]) => ({ type, score: Number(score.toFixed(2)) }))
if (ranked.length === 0) return { primary: null, ranked }
const [first, second] = ranked
const confidence = !second ? 'high' : first.score >= second.score * 1.5 ? 'high' : 'medium'
return { primary: { ...first, confidence }, ranked }
}
function suggestEnvTargets (primaryType, envKeys) {
if (!primaryType) return []
const preferred = envKeyByType[primaryType] || []
const existing = preferred.filter((key) => envKeys[key])
return existing.length > 0 ? existing : preferred.slice(0, 1)
}
function suggestCreateCommand (primaryType) {
const type = primaryType || 'postgresql'
const safeType = type === 'mysql' ? 'mysql' : type
return `sealos-cli database create ${safeType} --name <name> --cpu 1 --memory 1 --storage 3 --replicas 1 -o json`
}
if (!existsSync(root)) {
console.error(JSON.stringify({ ok: false, error: `Path does not exist: ${root}` }, null, 2))
process.exit(1)
}
const files = walk(root)
const { envFiles, keys: envKeys } = scanEnvFiles(files)
const packageInfo = scanPackageJson()
const migrations = detectMigrations(files)
const { scores, findings } = scoreFiles(files)
const { primary, ranked } = choosePrimary(scores)
const suggestedEnvKeys = suggestEnvTargets(primary?.type, envKeys)
const output = {
ok: true,
project: root,
recommendation: {
databaseType: primary?.type || 'postgresql',
confidence: primary?.confidence || 'low',
reason: primary ? 'Detected project database signals.' : 'No database-specific signal found; postgresql is the default only if the user wants a new relational database.',
suggestedEnvKeys,
createCommand: suggestCreateCommand(primary?.type)
},
existingEnv: {
files: envFiles,
databaseKeys: Object.fromEntries(
Object.entries(envKeys)
.filter(([key]) => /DATABASE|POSTGRES|MYSQL|MONGO|REDIS|QDRANT|WEAVIATE|CLICKHOUSE|CACHE|QUEUE|KV/.test(key))
)
},
package: packageInfo
? {
packageManager: packageInfo.packageManager,
databaseDependencies: Object.keys(packageInfo.dependencies || {}).filter((name) =>
/(prisma|drizzle|typeorm|sequelize|mongoose|mongodb|pg$|mysql|mysql2|redis|ioredis|upstash|qdrant|weaviate|clickhouse)/i.test(name)
),
migrationScripts: Object.fromEntries(
Object.entries(packageInfo.scripts || {}).filter(([name, value]) =>
/(db|database|migrate|migration|prisma|drizzle)/i.test(`${name} ${value}`)
)
)
}
: null,
migrations,
rankedSignals: ranked,
findings
}
console.log(JSON.stringify(output, null, 2))