Deployment Lifecycle Management#
Dokploy tracks every deployment execution — whether triggered manually, via webhook, or by a cron schedule — as a row in the deployment table. The lifecycle spans creation, execution, terminal resolution, and, on server restart, automated reconciliation of any interrupted runs.
Deployment Record & Status States#
The deployment schema defines four possible statuses :
| Status | Meaning |
|---|---|
running | Actively executing (default on creation) |
done | Completed successfully |
error | Failed with an error |
cancelled | Interrupted (e.g., server restart) |
Each record also stores :
logPath— path to the execution log filepid— process ID (set fordokploy-serverandserverschedule types)startedAt/finishedAt— timing metadataerrorMessage— terminal error detail- Foreign keys to the owning entity:
applicationId,composeId,serverId,previewDeploymentId,scheduleId,backupId,rollbackId, orvolumeBackupId
finishedAt is written automatically by updateDeploymentStatus when a deployment reaches a terminal state (done or error).
Deployment Queue (Self-Hosted)#
Self-hosted Dokploy dispatches jobs through a custom in-memory queue — not Redis/BullMQ . Key properties:
- Per-partition concurrency — jobs partition by
serverId; the local server usesLOCAL_PARTITION. - Per-group FIFO — jobs for the same application or compose project serialize within their group, so the same service never builds in parallel .
- DB-driven concurrency limit — resolved at runtime via
resolveBuildsConcurrency()from the settings table. - Cloud bypasses the queue — cloud deployments run directly in the background .
The queue is started at boot via startDeploymentWorker() and job processing is handled by processDeploymentJob(), which dispatches to application, compose, or preview deployment handlers.
Cron-Based Schedules#
User-defined recurring schedules are stored in the schedule table and support four target types :
scheduleType | Target |
|---|---|
application | docker exec into an application container |
compose | docker exec into a specific compose service container |
server | Runs a script.sh on a remote server via SSH |
dokploy-server | Runs a script.sh locally on the Dokploy host |
Each schedule record stores a cronExpression, optional timezone (defaults to UTC), shellType (bash/sh), command or script, and an enabled flag .
scheduleJob() registers each schedule with node-schedule using the schedule's scheduleId as the job name. When a cron fires, runCommand() is invoked, which:
- Creates a new
deploymentrecord viacreateDeploymentSchedule(status:running) - Resolves the target container or server and executes the command, streaming output to the
logPath - Calls
updateDeploymentStatusto mark itdoneorerror
If a container is not found, the deployment is immediately errored with a descriptive message .
For dokploy-server and server schedule types, the PID of the spawned process is parsed from stdout and written back to the deployment record via updateDeployment .
Schedules are loaded at startup by initSchedules(), which queries all enabled=true schedules and registers each via scheduleJob(). removeScheduleJob() cancels a specific job by scheduleId.
Startup Reconciliation & Recovery#
Because the queue is in-memory, any deployment in running state when the process exits has no associated worker. On server boot, initCancelDeployments() performs a bulk reconciliation:
- Sets all
status = 'running'deployments →status = 'cancelled' - Resets
applicationStatus → 'idle'for all affected applications - Resets
composeStatus → 'idle'for all affected compose projects
This prevents services from remaining stuck in a "deploying" state in the UI after a restart.
The full server startup sequence (self-hosted only) is :
initCancelDeployments() ← reconcile stale running deployments
initSchedules() ← register cron jobs from DB
startDeploymentWorker() ← start in-memory queue worker
Note on legacy Redis state: Instances that previously used BullMQ may retain stale
bull:deployments:*keys in Redis. These must be cleared manually;initCancelDeploymentsonly reconciles Postgres records, not Redis queue state. See the Core Infrastructure doc for the cleanup commands.
Key Source Files#
| File | Purpose |
|---|---|
packages/server/src/db/schema/deployment.ts | Deployment table schema, status enum, API schemas |
packages/server/src/db/schema/schedule.ts | Schedule table schema and types |
packages/server/src/utils/schedules/utils.ts | scheduleJob, removeScheduleJob, runCommand |
packages/server/src/utils/schedules/index.ts | initSchedules — loads all enabled schedules on boot |
packages/server/src/utils/startup/cancel-deployments.ts | initCancelDeployments — startup stale-run reconciliation |
apps/dokploy/server/queues/in-memory-queue.ts | In-memory job queue implementation |
apps/dokploy/server/queues/queueSetup.ts | Queue setup, startDeploymentWorker, SIGTERM handling |
apps/dokploy/server/queues/deployments-queue.ts | processDeploymentJob — dispatches jobs to deployment handlers |
packages/server/src/services/deployment.ts | createDeploymentSchedule, updateDeploymentStatus, updateDeployment |