coollabsio/coolify · error · DeploymentException
Command execution failed (exit code {$process_result->exitCo
Error message
Command execution failed (exit code {$process_result->exitCode()}): {$redactedCommand}
Error: {$error} What it means
Coolify runs every deployment step over SSH on the destination server via the ExecuteRemoteCommand trait (Symfony Process wrapping an SSH multiplexed command). When the process finishes with a non-zero exit code and ignore_errors is false, it throws DeploymentException containing the exit code, the command with secrets redacted via redact_sensitive_info(), and the stderr output (falling back to stdout, then to 'Command failed with no error output'). ApplicationDeploymentJob catches it, marks the deployment queue row as failed, and preserves the logs. A special exit code 69420 instead means 'Deployment cancelled by user'.
Source
Thrown at app/Traits/ExecuteRemoteCommand.php:243
$process_result = $process->wait();
if ($process_result->exitCode() !== 0) {
if (! $ignore_errors) {
// Check if deployment was cancelled while command was running
if (isset($this->application_deployment_queue)) {
$this->application_deployment_queue->refresh();
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
throw new \RuntimeException('Deployment cancelled by user', 69420);
}
}
// Don't immediately set to FAILED - let the retry logic handle it
// This prevents premature status changes during retryable SSH errors
$error = $process_result->errorOutput();
if (empty($error)) {
$error = $process_result->output() ?: 'Command failed with no error output';
}
$redactedCommand = $this->redact_sensitive_info($command);
throw new DeploymentException("Command execution failed (exit code {$process_result->exitCode()}): {$redactedCommand}\nError: {$error}");
}
}
}
/**
* Add a log entry for SSH retry attempts
*/
private function addRetryLogEntry(int $attempt, int $maxRetries, int $delay, string $errorMessage)
{
$retryMessage = "SSH connection failed. Retrying... (Attempt {$attempt}/{$maxRetries}, waiting {$delay}s)\nError: {$errorMessage}";
$new_log_entry = [
'output' => $this->redact_sensitive_info($retryMessage),
'type' => 'stdout',
'timestamp' => Carbon::now('UTC'),
'hidden' => false,
'batch' => static::$batch_counter,
];View on GitHub (pinned to 70b9acc424)
Solutions
- Open the deployment logs and read the 'Error:' section under the failing command — it is the command's stderr and names the root cause.
- SSH into the destination server and re-run the redacted command shown in the message to reproduce the failure interactively.
- Check server health: df -h for disk space, docker ps to confirm the daemon, and curl to the registry from the server.
- For git failures, verify the branch/tag exists and that the deploy key/token still has access to the repository.
- Fix the underlying cause (Dockerfile, build pack command, env vars) and redeploy; transient SSH errors are retried automatically by the SSH retry logic before this exception surfaces.
Example fix
// before: deployment fails with // Command execution failed (exit code 1): git clone -b $branch ... // Error: Remote branch main not found in upstream origin // after: point the application at a branch that exists, then redeploy $application->update(['git_branch' => 'master']); $application->deploy();
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the destination server before triggering a deployment use App\Models\Server; $server = Server::findOrFail($destination->server_id); instant_remote_process(['docker info >/dev/null && df -h / | tail -1'], $server); // throws early with a clearer error than a mid-deploy failure
Try / catch
try {
$application->deploy();
} catch (\App\Exceptions\DeploymentException $e) {
// message contains exit code, redacted command, and stderr
report($e);
// mark the attempt failed in your tracker; keep deployment logs for triage
} Prevention
- Build and run the Dockerfile locally before pushing so exit-code surprises surface before deploy
- Keep deploy keys/tokens valid and scoped; rotate before expiry
- Monitor disk space on destination servers — full disks are a classic non-zero exit source
- Pin base image tags and registry credentials to avoid pull-time failures
When it happens
Trigger: Any deployment command that exits non-zero: git clone/pull failing on a missing branch, missing deploy key or expired token; docker build failing on a bad Dockerfile or unpullable base image; docker compose up failing; a run command in the build pack exiting non-zero; the destination server having a full disk or a stopped Docker daemon.
Common situations: Private repo without a working deploy key; typo'd branch/tag or image tag; missing build arg or environment variable used during build; registry rate limits or network problems on the server; disk exhausted so layers cannot be written; server Docker daemon down while SSH itself still works.
Related errors
- Pre-deployment command: Could not find a valid container. Is
- Post-deployment command: Could not find a valid container. I
- Server OS type is not supported for automated installation.
- Server OS type is not supported. Please install Docker manua
- Invalid dockerfile_target_build: contains forbidden characte
AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17).
Data as JSON: /api/errors/0a212dd4cf709ca1.
Report an issue: GitHub.