一键导入
deployment-system
Comprehensive guide to how Docklift builds and deploys user applications.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Comprehensive guide to how Docklift builds and deploys user applications.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Guide for server management, system APIs, backups, and maintenance operations.
Guide for developing features in the Vite + React Router frontend.
Guide to Docklift's automated release pipeline using semantic-release.
Guide for setting up, running, and developing the Docklift project.
Guide for setting up and managing Docklift's GitHub App integration.
Coolify/Dokploy-style managed databases with Dokku-style app linking.
| name | Deployment System |
| description | Comprehensive guide to how Docklift builds and deploys user applications. |
This guide details the lifecycle of a deployment in Docklift, from source code to running container.
routes/projects.ts: Project CRUD, build settings, persistent volumes, domain assignment.routes/deployments.ts: Deployment history plus the streaming deploy / stop / restart / redeploy / cancel handlers.lib/runCompose.ts: Shared docker spawn with mandatory error + close handlers (never leave unhandled spawn errors).lib/deploymentState.ts: In-memory “deploying” lock per project (isProjectDeploying / setProjectDeploying).lib/projectStatusSync.ts: Inspect all service containers; aggregate project status
(running / stopped / error / degraded when some running + some stopped).lib/deploymentRecovery.ts: On boot, mark stale in_progress failed and stuck building projects corrected.lib/portAllocation.ts: Transactional host-port claim (only when publish_host_port is true).services/docker.ts: inspect/logs + connectProxyToProjectNetwork (throws on failure) /
disconnectProxyFromProjectNetwork (before stop/cancel/delete) / teardownProjectNetwork.services/buildResolver.ts: Decides what to build (Dockerfile vs Railpack, base directory, service list).services/buildRunner.ts: Builds image; public build args vs is_secret → BuildKit --secret.services/compose.ts: Scans Dockerfiles (dedupes colliding service names with path hash) and writes
runtime Compose on a per-project network (labels, no-new-privileges, opt-in host ports;
no default cap_drop: ALL / hard mem-cpu caps — optional via compose options).services/git.ts: Clone / pull + scrubOriginRemote.lib/naming.ts: Compose project, container, image, and storageVolumeComposeKey names.Trigger
POST /api/deployments/:projectId/deploy (streaming response).deployment.status = in_progress → 409.
stop / restart also 409 while deploying; caller must cancel first.projects.ts + isProjectDeploying).Preparation
status: in_progress).deployments/<projectId>/:
git clone, or git fetch + reset --hard + clean for an existing checkout.lib/safeUnzip.ts (rejects traversal entries).finally block, even when extraction throws.Git Token Security (GitHub projects):
getInstallationToken().scrubOriginRemote) and verified clean..git/config).spawn / spawnSync with argument arrays — never string interpolation.Build Resolution
build_type on the project is auto (default), dockerfile, or railpack.auto prefers a repository Dockerfile and falls back to Railpack when none is found.base_directory (default .) scopes detection to a monorepo subdirectory. It is resolved
with resolveProjectPath(), which rejects any path escaping the deployment root.dockerfile_path.Image Build (buildServiceImage)
--build-arg only for vars with is_build_arg and not is_secret;
validateDockerBuildArgs() warns when ARG is missing.is_secret → docker buildx build --secret id=KEY,env=KEY (never --build-arg).
Missing RUN --mount=type=secret,id=KEY is a preflight failure (deploy aborts).docker buildx build with plan JSON; build vars as BuildKit secrets + secrets-hash.summarizeBuildFailure(), which turns common toolchain noise into one
actionable line (e.g. an out-of-sync package-lock.json on npm ci).Run
deployments/.docklift/<projectId>/compose.yml.
Source files are never patched — no repository Dockerfile or docker-compose.yml is
rewritten, which means user-committed compose files stay intact.dl-net-<shortId> (not the control-plane docklift_network).publish_host_port === true; otherwise omit ports:.security_opt: no-new-privileges, labels com.docklift.*.
Optional memLimit / cpus via compose options (not applied by default — DB images need room).docker compose -f <runtime-compose> -p <composeProject> up -d --remove-orphansconnectProxyToProjectNetwork(projectId) — on failure mark deploy
failed and do not activate domains (never log false “proxy attached”).disconnectProxyFromProjectNetwork before compose down
(proxy endpoint otherwise blocks network removal). If teardown ultimately fails, reconnect
the proxy so running apps keep domain routing.compose down, verify with exact labels
com.docker.compose.project=<alias> (containers + networks). Never trust stderr “not found”.
Abort delete with 409 if owned resources remain.Verification
syncProjectStatusFromContainers() updates each service from Docker, then aggregates:
all running → running; any error → error; some running + some stopped → degraded;
else stopped. Never report full running for a mixed fleet.setProjectDeploying) is held through status write and nginx/SSL activation.updateMany with status ≠ cancelled so cancel cannot be overwritten as success.failDeploymentState likewise never overwrites cancelled.Cancel (anytime)
compose down OK).in_progress rows cancelled. Idle cancel must not rewrite past
success / failed history.stopped when teardown succeeds.build_type | Behaviour |
|---|---|
auto | Repository Dockerfile if present, else Railpack detection |
dockerfile | Requires a Dockerfile; fails loudly instead of silently falling back |
railpack | Always Railpack, even when a Dockerfile exists |
detectManifests() reports which framework manifests were seen (e.g. package.json,
requirements.txt, pyproject.toml), which is what the UI shows as the detected stack.
Rebuilds replace containers, so anything written to a container's filesystem is lost unless mounted.
PersistentVolume rows
(service_name, mount_path, display_name).dl-<shortId>-<slug>-<hash(label)> so labels like a-b vs a_b never collide.
Labelled com.docklift.project=<projectId> for cleanup.storageVolumeComposeKey(service, index, volumeName).docker compose down never deletes user data.DATABASE_URL need no volume — only in-container state
(SQLite files, uploads, caches) does.All streaming endpoints (deploy, stop, restart, redeploy) use a writeLog helper with a
disconnection guard:
const writeLog = (text: string) => {
try { if (!res.writableEnded) res.write(text); } catch {}
logs.push(text);
};
This prevents crashes when the client disconnects mid-stream and guarantees the deployment status is persisted regardless of connection state.
deployments/
<projectId>/ # Application source (clone/upload root; never modified by Docklift)
.docklift/
<projectId>/
compose.yml # Generated runtime state
*-railpack-*.json # Generated Railpack build plans
.env # Runtime environment variables (when used)
Always use lib/naming.ts — never hand-build these strings.
| Thing | Format | Example |
|---|---|---|
Compose project (-p) | dl-<slug>-<shortId> | dl-python-smoke-53b01966 |
| Container | dl_<slug>_<shortId>_<svc> | dl_python-smoke_53b01966_app |
| Project network | dl-net-<shortId> | dl-net-53b01966 |
| Volume | dl-<shortId>-<slug> | dl-53b01966-data |
shortId is the first 8 hex chars of the project UUID (dashes stripped). composeProjectAliases()
also returns the bare project UUID, the legacy compose project name, so older deployments can still
be torn down.
Control plane (docklift-*) → docklift_network. User apps → per-project dl-net-* only
(proxy attached after up).
isValidEnvKey; unique (project_id, key) after startup/prepareDb dedupe.docker logs <container>.internal_port.POST /:projectId/cancel first.in_progress / building after backend restart: recoverDeploymentStateOnBoot() marks
interrupted deployments failed and reconciles project status from containers.