diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index eaee7e221..f06b05428 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -560,6 +560,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue $this->application_deployment_queue->addLogEntry("Starting deployment of {$displayName} to {$this->server->name}."); $this->generate_image_names(); $this->prepare_builder_image(); + $this->detect_image_healthcheck(); $this->generate_compose_file(); // Save runtime environment variables (including empty .env file if no variables defined) @@ -1157,6 +1158,43 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue } } + private function detect_image_healthcheck() + { + $this->execute_remote_command( + [ + "docker pull {$this->production_image_name} 2>/dev/null", + 'hidden' => true, + 'ignore_errors' => true, + ], + [ + "docker inspect --format='{{json .Config.Healthcheck}}' {$this->production_image_name} 2>/dev/null", + 'hidden' => true, + 'save' => 'image_healthcheck', + 'ignore_errors' => true, + ], + ); + + $output = trim($this->saved_outputs->get('image_healthcheck') ?? ''); + $has_healthcheck = false; + + if (! empty($output) && $output !== 'null' && $output !== '') { + $healthcheck = json_decode($output, true); + if (is_array($healthcheck) && isset($healthcheck['Test'])) { + $test = $healthcheck['Test']; + // HEALTHCHECK NONE → {"Test":["NONE"]} + $has_healthcheck = ! (is_array($test) && count($test) === 1 && strtoupper($test[0]) === 'NONE'); + } + } + + if ($has_healthcheck !== $this->application->custom_healthcheck_found) { + $this->application->custom_healthcheck_found = $has_healthcheck; + $this->application->save(); + if ($has_healthcheck) { + $this->application_deployment_queue->addLogEntry('Custom healthcheck detected in Docker image.'); + } + } + } + private function generate_runtime_environment_variables() { $envs = collect([]); @@ -1788,13 +1826,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue if ($this->server->isSwarm()) { // Implement healthcheck for swarm } else { - if ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found === false) { + if ($this->application->isHealthcheckDisabled()) { $this->newVersionIsHealthy = true; return; } if ($this->application->custom_healthcheck_found) { - $this->application_deployment_queue->addLogEntry('Custom healthcheck found in Dockerfile.'); + $this->application_deployment_queue->addLogEntry('Using custom healthcheck from image.'); } if ($this->container_name) { $counter = 1; @@ -2553,9 +2591,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue // Always use .env file $docker_compose['services'][$this->container_name]['env_file'] = ['.env']; - // Only add Coolify healthcheck if no custom HEALTHCHECK found in Dockerfile - // If custom_healthcheck_found is true, the Dockerfile's HEALTHCHECK will be used - // If healthcheck is disabled, no healthcheck will be added + // Healthcheck logic: + // - HC enabled + no image HC → inject Coolify's curl/wget check + // - HC enabled + image has HC → let Docker use image's own HEALTHCHECK + // - HC disabled + image has HC → explicitly disable it in compose + // - HC disabled + no image HC → no healthcheck at all if (! $this->application->custom_healthcheck_found && ! $this->application->isHealthcheckDisabled()) { $docker_compose['services'][$this->container_name]['healthcheck'] = [ 'test' => [ @@ -2567,6 +2607,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue 'retries' => $this->application->health_check_retries, 'start_period' => $this->application->health_check_start_period.'s', ]; + } elseif ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found) { + $docker_compose['services'][$this->container_name]['healthcheck'] = [ + 'disable' => true, + ]; } if (! is_null($this->application->limits_cpuset)) { @@ -3218,7 +3262,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); }); } } else { - if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') { + if (($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') && ! $this->application->custom_healthcheck_found) { $this->application_deployment_queue->addLogEntry('----------------------------------------'); $this->application_deployment_queue->addLogEntry("WARNING: Dockerfile or Docker Image based deployment detected. The healthcheck needs a curl or wget command to check the health of the application. Please make sure that it is available in the image or turn off healthcheck on Coolify's UI."); $this->application_deployment_queue->addLogEntry('----------------------------------------'); diff --git a/resources/views/livewire/project/shared/health-checks.blade.php b/resources/views/livewire/project/shared/health-checks.blade.php index 730353c87..049345f54 100644 --- a/resources/views/livewire/project/shared/health-checks.blade.php +++ b/resources/views/livewire/project/shared/health-checks.blade.php @@ -15,9 +15,13 @@
Define how your resource's health should be checked.
- @if ($customHealthcheckFound) - -

A custom health check has been detected. If you enable this health check, it will disable the custom one and use this instead.

+ @if ($customHealthcheckFound && $healthCheckEnabled) + +

A custom health check was detected in the Docker image and is being used. The settings below are not applied. To use Coolify's health check instead, disable the healthcheck and re-enable it after removing the custom one from the image.

+
+ @elseif ($customHealthcheckFound && !$healthCheckEnabled) + +

A custom health check was detected in the Docker image, but healthcheck is disabled. The image's health check will not run. Enable healthcheck to use the image's custom health check.

@endif