fix: docker cleanup job

This commit is contained in:
Andras Bacsai
2024-08-26 12:23:03 +02:00
parent 4d7877ce1d
commit 3d73c98779
10 changed files with 231 additions and 179 deletions

View File

@@ -12,29 +12,21 @@ class CleanupDocker
public function handle(Server $server) public function handle(Server $server)
{ {
$commands = $this->getCommands($force); $commands = $this->getCommands();
foreach ($commands as $command) { foreach ($commands as $command) {
instant_remote_process([$command], $server, false); instant_remote_process([$command], $server, false);
} }
} }
private function getCommands(bool $force): array private function getCommands(): array
{ {
$commonCommands = [ $commonCommands = [
'docker container prune -f --filter "label=coolify.managed=true"', 'docker container prune -f --filter "label=coolify.managed=true"',
'docker image prune -f', 'docker image prune -af',
'docker builder prune -f', 'docker builder prune -af',
]; ];
if ($force) {
return array_merge([
'docker container prune -f --filter "label=coolify.managed=true"',
'docker image prune -af',
'docker builder prune -af',
], $commonCommands);
}
return $commonCommands; return $commonCommands;
} }
} }

View File

@@ -106,7 +106,11 @@ class Kernel extends ConsoleKernel
//The lines below need to be added as soon as timzone is merged!! //The lines below need to be added as soon as timzone is merged!!
//$serverTimezone = $server->settings->server_timezone; //$serverTimezone = $server->settings->server_timezone;
//$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer(); //$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->onOneServer(); if ($server->settings->force_docker_cleanup) {
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->onOneServer();
} else {
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
}
} }
} }
@@ -118,7 +122,7 @@ class Kernel extends ConsoleKernel
return; return;
} }
foreach ($scheduled_backups as $scheduled_backup) { foreach ($scheduled_backups as $scheduled_backup) {
if (!$scheduled_backup->enabled) { if (! $scheduled_backup->enabled) {
continue; continue;
} }
if (is_null(data_get($scheduled_backup, 'database'))) { if (is_null(data_get($scheduled_backup, 'database'))) {
@@ -150,7 +154,7 @@ class Kernel extends ConsoleKernel
$service = $scheduled_task->service; $service = $scheduled_task->service;
$application = $scheduled_task->application; $application = $scheduled_task->application;
if (!$application && !$service) { if (! $application && ! $service) {
ray('application/service attached to scheduled task does not exist'); ray('application/service attached to scheduled task does not exist');
$scheduled_task->delete(); $scheduled_task->delete();
@@ -177,7 +181,7 @@ class Kernel extends ConsoleKernel
protected function commands(): void protected function commands(): void
{ {
$this->load(__DIR__ . '/Commands'); $this->load(__DIR__.'/Commands');
require base_path('routes/console.php'); require base_path('routes/console.php');
} }

View File

@@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -17,14 +18,24 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 300; public $timeout = 600;
public $tries = 2; public $tries = 1;
public ?string $usageBefore = null; public ?string $usageBefore = null;
public function __construct(public Server $server) {} public function __construct(public Server $server) {}
public function middleware(): array
{
return [new WithoutOverlapping($this->server->id)];
}
public function uniqueId(): int
{
return $this->server->id;
}
public function handle(): void public function handle(): void
{ {
try { try {
@@ -32,34 +43,30 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
return; return;
} }
if ($this->server->settings->force_docker_cleanup) { if ($this->server->settings->force_docker_cleanup) {
Log::info('DockerCleanupJob force cleanup on ' . $this->server->name); Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
CleanupDocker::run(server: $this->server, force: true); CleanupDocker::run(server: $this->server);
return; return;
} }
return;
}
try {
$this->usageBefore = $this->server->getDiskUsage(); $this->usageBefore = $this->server->getDiskUsage();
if (str($this->usageBefore)->isEmpty() || $this->usageBefore === null || $this->usageBefore === 0) { if (str($this->usageBefore)->isEmpty() || $this->usageBefore === null || $this->usageBefore === 0) {
Log::info('DockerCleanupJob force cleanup on ' . $this->server->name); Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
CleanupDocker::run(server: $this->server, force: true); CleanupDocker::run(server: $this->server);
return; return;
} }
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) { if ($this->usageBefore >= $this->server->settings->docker_cleanup_threshold) {
CleanupDocker::run(server: $this->server); CleanupDocker::run(server: $this->server);
$usageAfter = $this->server->getDiskUsage(); $usageAfter = $this->server->getDiskUsage();
if ($usageAfter < $this->usageBefore) { if ($usageAfter < $this->usageBefore) {
$this->server->team?->notify(new DockerCleanup($this->server, 'Saved ' . ($this->usageBefore - $usageAfter) . '% disk space.')); $this->server->team?->notify(new DockerCleanup($this->server, 'Saved '.($this->usageBefore - $usageAfter).'% disk space.'));
Log::info('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name); Log::info('DockerCleanupJob done: Saved '.($this->usageBefore - $usageAfter).'% disk space on '.$this->server->name);
} else { } else {
Log::info('DockerCleanupJob failed to save disk space on ' . $this->server->name); Log::info('DockerCleanupJob failed to save disk space on '.$this->server->name);
} }
} else { } else {
Log::info('No need to clean up ' . $this->server->name); Log::info('No need to clean up '.$this->server->name);
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
CleanupDocker::run(server: $this->server); CleanupDocker::run(server: $this->server);

View File

@@ -18,8 +18,6 @@ class Form extends Component
public ?string $wildcard_domain = null; public ?string $wildcard_domain = null;
public int $cleanup_after_percentage;
public bool $dockerInstallationStarted = false; public bool $dockerInstallationStarted = false;
public bool $revalidate = false; public bool $revalidate = false;
@@ -81,7 +79,7 @@ class Form extends Component
{ {
if ($field === 'server.settings.docker_cleanup_frequency') { if ($field === 'server.settings.docker_cleanup_frequency') {
$frequency = $this->server->settings->docker_cleanup_frequency; $frequency = $this->server->settings->docker_cleanup_frequency;
if (empty($frequency) || !validate_cron_expression($frequency)) { if (empty($frequency) || ! validate_cron_expression($frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.'); $this->dispatch('error', 'Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.');
$this->server->settings->docker_cleanup_frequency = '*/10 * * * *'; $this->server->settings->docker_cleanup_frequency = '*/10 * * * *';
} }
@@ -169,7 +167,7 @@ class Form extends Component
$this->server->settings->save(); $this->server->settings->save();
$this->dispatch('proxyStatusUpdated'); $this->dispatch('proxyStatusUpdated');
} else { } else {
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: ' . $error); $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$error);
return; return;
} }
@@ -185,31 +183,35 @@ class Form extends Component
public function submit() public function submit()
{ {
if (isCloud() && !isDev()) { try {
$this->validate(); if (isCloud() && ! isDev()) {
$this->validate([ $this->validate();
'server.ip' => 'required', $this->validate([
]); 'server.ip' => 'required',
} else { ]);
$this->validate(); } else {
} $this->validate();
$uniqueIPs = Server::all()->reject(function (Server $server) { }
return $server->id === $this->server->id; $uniqueIPs = Server::all()->reject(function (Server $server) {
})->pluck('ip')->toArray(); return $server->id === $this->server->id;
if (in_array($this->server->ip, $uniqueIPs)) { })->pluck('ip')->toArray();
$this->dispatch('error', 'IP address is already in use by another team.'); if (in_array($this->server->ip, $uniqueIPs)) {
$this->dispatch('error', 'IP address is already in use by another team.');
return; return;
}
refresh_server_connection($this->server->privateKey);
$this->server->settings->wildcard_domain = $this->wildcard_domain;
if ($this->server->settings->force_docker_cleanup) {
$this->server->settings->docker_cleanup_frequency = $this->server->settings->docker_cleanup_frequency;
} else {
$this->server->settings->docker_cleanup_threshold = $this->server->settings->docker_cleanup_threshold;
}
$this->server->settings->save();
$this->server->save();
$this->dispatch('success', 'Server updated.');
} catch (\Throwable $e) {
return handleError($e, $this);
} }
refresh_server_connection($this->server->privateKey);
$this->server->settings->wildcard_domain = $this->wildcard_domain;
if ($this->server->settings->force_docker_cleanup) {
$this->server->settings->docker_cleanup_frequency = $this->server->settings->docker_cleanup_frequency;
} else {
$this->server->settings->docker_cleanup_threshold = $this->server->settings->docker_cleanup_threshold;
}
$this->server->settings->save();
$this->server->save();
$this->dispatch('success', 'Server updated.');
} }
} }

View File

@@ -2,13 +2,11 @@
namespace App\Models; namespace App\Models;
use App\Enums\ProxyTypes;
use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
use Spatie\Url\Url; use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
@@ -25,7 +23,7 @@ use Symfony\Component\Yaml\Yaml;
'description' => ['type' => 'string', 'description' => 'The description of the service.'], 'description' => ['type' => 'string', 'description' => 'The description of the service.'],
'docker_compose_raw' => ['type' => 'string', 'description' => 'The raw docker-compose.yml file of the service.'], 'docker_compose_raw' => ['type' => 'string', 'description' => 'The raw docker-compose.yml file of the service.'],
'docker_compose' => ['type' => 'string', 'description' => 'The docker-compose.yml file that is parsed and modified by Coolify.'], 'docker_compose' => ['type' => 'string', 'description' => 'The docker-compose.yml file that is parsed and modified by Coolify.'],
'destination_type' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'], 'destination_type' => ['type' => 'string', 'description' => 'Destination type.'],
'destination_id' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'], 'destination_id' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'],
'connect_to_docker_network' => ['type' => 'boolean', 'description' => 'The flag to connect the service to the predefined Docker network.'], 'connect_to_docker_network' => ['type' => 'boolean', 'description' => 'The flag to connect the service to the predefined Docker network.'],
'is_container_label_escape_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label escape.'], 'is_container_label_escape_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label escape.'],

View File

@@ -44,7 +44,7 @@ class DockerCleanup extends Notification implements ShouldQueue
// $mail->view('emails.high-disk-usage', [ // $mail->view('emails.high-disk-usage', [
// 'name' => $this->server->name, // 'name' => $this->server->name,
// 'disk_usage' => $this->disk_usage, // 'disk_usage' => $this->disk_usage,
// 'threshold' => $this->cleanup_after_percentage, // 'threshold' => $this->docker_cleanup_threshold,
// ]); // ]);
// return $mail; // return $mail;
// } // }

View File

@@ -17,7 +17,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
public $tries = 1; public $tries = 1;
public function __construct(public Server $server, public int $disk_usage, public int $cleanup_after_percentage) {} public function __construct(public Server $server, public int $disk_usage, public int $docker_cleanup_threshold) {}
public function via(object $notifiable): array public function via(object $notifiable): array
{ {
@@ -46,7 +46,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
$mail->view('emails.high-disk-usage', [ $mail->view('emails.high-disk-usage', [
'name' => $this->server->name, 'name' => $this->server->name,
'disk_usage' => $this->disk_usage, 'disk_usage' => $this->disk_usage,
'threshold' => $this->cleanup_after_percentage, 'threshold' => $this->docker_cleanup_threshold,
]); ]);
return $mail; return $mail;
@@ -54,7 +54,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
public function toDiscord(): string public function toDiscord(): string
{ {
$message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup."; $message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->docker_cleanup_threshold}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.";
return $message; return $message;
} }
@@ -62,7 +62,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
public function toTelegram(): array public function toTelegram(): array
{ {
return [ return [
'message' => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.", 'message' => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->docker_cleanup_threshold}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.",
]; ];
} }
} }

View File

@@ -1,5 +1,6 @@
<?php <?php
use App\Models\ServerSetting;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
@@ -13,16 +14,23 @@ class AddServerCleanupFieldsToServerSettingsTable extends Migration
*/ */
public function up() public function up()
{ {
$serverSettings = ServerSetting::all();
Schema::table('server_settings', function (Blueprint $table) { Schema::table('server_settings', function (Blueprint $table) {
$table->boolean('force_docker_cleanup')->default(false); $table->boolean('force_docker_cleanup')->default(false);
$table->string('docker_cleanup_frequency')->default('*/10 * * * *'); $table->string('docker_cleanup_frequency')->default('*/10 * * * *');
$table->integer('docker_cleanup_threshold')->default(80); $table->integer('docker_cleanup_threshold')->default(80);
// Remove old columns // Remove old columns
$table->dropColumn('cleanup_after_percentage'); $table->dropColumn('cleanup_after_percentage');
$table->dropColumn('is_force_cleanup_enabled'); $table->dropColumn('is_force_cleanup_enabled');
}); });
foreach ($serverSettings as $serverSetting) {
$serverSetting->force_docker_cleanup = $serverSetting->is_force_cleanup_enabled;
$serverSetting->docker_cleanup_threshold = $serverSetting->cleanup_after_percentage;
$serverSetting->save();
}
} }
/** /**
@@ -32,15 +40,21 @@ class AddServerCleanupFieldsToServerSettingsTable extends Migration
*/ */
public function down() public function down()
{ {
$serverSettings = ServerSetting::all();
Schema::table('server_settings', function (Blueprint $table) { Schema::table('server_settings', function (Blueprint $table) {
$table->dropColumn('force_docker_cleanup'); $table->dropColumn('force_docker_cleanup');
$table->dropColumn('docker_cleanup_frequency'); $table->dropColumn('docker_cleanup_frequency');
$table->dropColumn('docker_cleanup_threshold'); $table->dropColumn('docker_cleanup_threshold');
// Add back old columns // Add back old columns
$table->integer('cleanup_after_percentage')->default(80); $table->integer('cleanup_after_percentage')->default(80);
$table->boolean('force_server_cleanup')->default(false); $table->boolean('force_server_cleanup')->default(false);
$table->boolean('is_force_cleanup_enabled')->default(false);
}); });
foreach ($serverSettings as $serverSetting) {
$serverSetting->is_force_cleanup_enabled = $serverSetting->force_docker_cleanup;
$serverSetting->cleanup_after_percentage = $serverSetting->docker_cleanup_threshold;
$serverSetting->save();
}
} }
} }

View File

@@ -4523,14 +4523,14 @@ components:
properties: properties:
id: id:
type: integer type: integer
cleanup_after_percentage:
type: integer
concurrent_builds: concurrent_builds:
type: integer type: integer
dynamic_timeout: dynamic_timeout:
type: integer type: integer
force_disabled: force_disabled:
type: boolean type: boolean
force_server_cleanup:
type: boolean
is_build_server: is_build_server:
type: boolean type: boolean
is_cloudflare_tunnel: is_cloudflare_tunnel:
@@ -4577,6 +4577,10 @@ components:
type: integer type: integer
metrics_token: metrics_token:
type: string type: string
docker_cleanup_frequency:
type: string
docker_cleanup_threshold:
type: integer
server_id: server_id:
type: integer type: integer
wildcard_domain: wildcard_domain:
@@ -4613,6 +4617,9 @@ components:
docker_compose: docker_compose:
type: string type: string
description: 'The docker-compose.yml file that is parsed and modified by Coolify.' description: 'The docker-compose.yml file that is parsed and modified by Coolify.'
destination_type:
type: string
description: 'Destination type.'
destination_id: destination_id:
type: integer type: integer
description: 'The unique identifier of the destination where the service is running.' description: 'The unique identifier of the destination where the service is running.'

View File

@@ -3,68 +3,73 @@
<div class="flex gap-2"> <div class="flex gap-2">
<h2>General</h2> <h2>General</h2>
@if ($server->id === 0) @if ($server->id === 0)
<x-modal-confirmation buttonTitle="Save" title="Change Localhost" action="submit"> <x-modal-confirmation buttonTitle="Save" title="Change Localhost" action="submit">
You could lose a lot of functionalities if you change the server details of the server where Coolify You could lose a lot of functionalities if you change the server details of the server where Coolify
is is
running on.<br>Please think again. running on.<br>Please think again.
</x-modal-confirmation> </x-modal-confirmation>
@else @else
<x-forms.button type="submit">Save</x-forms.button> <x-forms.button type="submit">Save</x-forms.button>
@if ($server->isFunctional()) @if ($server->isFunctional())
<x-slide-over closeWithX fullScreen> <x-slide-over closeWithX fullScreen>
<x-slot:title>Validate & configure</x-slot:title> <x-slot:title>Validate & configure</x-slot:title>
<x-slot:content> <x-slot:content>
<livewire:server.validate-and-install :server="$server" ask /> <livewire:server.validate-and-install :server="$server" ask />
</x-slot:content> </x-slot:content>
<x-forms.button @click="slideOverOpen=true" wire:click.prevent='validateServer' isHighlighted> <x-forms.button @click="slideOverOpen=true" wire:click.prevent='validateServer' isHighlighted>
Revalidate server Revalidate server
</x-forms.button> </x-forms.button>
</x-slide-over> </x-slide-over>
@endif @endif
@endif @endif
</div> </div>
@if ($server->isFunctional()) @if ($server->isFunctional())
Server is reachable and validated. Server is reachable and validated.
@else @else
You can't use this server until it is validated. You can't use this server until it is validated.
@endif @endif
@if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id !== 0) @if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id !== 0)
<x-slide-over closeWithX fullScreen> <x-slide-over closeWithX fullScreen>
<x-slot:title>Validate & configure</x-slot:title> <x-slot:title>Validate & configure</x-slot:title>
<x-slot:content> <x-slot:content>
<livewire:server.validate-and-install :server="$server" /> <livewire:server.validate-and-install :server="$server" />
</x-slot:content> </x-slot:content>
<x-forms.button @click="slideOverOpen=true" class="w-full mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100" wire:click.prevent='validateServer' isHighlighted> <x-forms.button @click="slideOverOpen=true"
Validate Server & Install Docker Engine class="w-full mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100"
</x-forms.button> wire:click.prevent='validateServer' isHighlighted>
</x-slide-over> Validate Server & Install Docker Engine
@if ($server->validation_logs) </x-forms.button>
<h4>Previous Validation Logs</h4> </x-slide-over>
<div class="pb-8"> @if ($server->validation_logs)
{!! $server->validation_logs !!} <h4>Previous Validation Logs</h4>
</div> <div class="pb-8">
@endif {!! $server->validation_logs !!}
</div>
@endif
@endif @endif
@if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id === 0) @if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id === 0)
<x-forms.button class="mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100" wire:click.prevent='checkLocalhostConnection' isHighlighted> <x-forms.button class="mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100"
Validate Server wire:click.prevent='checkLocalhostConnection' isHighlighted>
</x-forms.button> Validate Server
</x-forms.button>
@endif @endif
@if ($server->isForceDisabled() && isCloud()) @if ($server->isForceDisabled() && isCloud())
<div class="pt-4 font-bold text-red-500">The system has disabled the server because you have exceeded the <div class="pt-4 font-bold text-red-500">The system has disabled the server because you have exceeded the
number of servers for which you have paid.</div> number of servers for which you have paid.</div>
@endif @endif
<div class="flex flex-col gap-2 pt-4"> <div class="flex flex-col gap-2 pt-4">
<div class="flex flex-col w-full gap-2 lg:flex-row"> <div class="flex flex-col w-full gap-2 lg:flex-row">
<x-forms.input id="server.name" label="Name" required /> <x-forms.input id="server.name" label="Name" required />
<x-forms.input id="server.description" label="Description" /> <x-forms.input id="server.description" label="Description" />
@if (!$server->settings->is_swarm_worker && !$server->settings->is_build_server) @if (!$server->settings->is_swarm_worker && !$server->settings->is_build_server)
<x-forms.input placeholder="https://example.com" id="wildcard_domain" label="Wildcard Domain" helper='A wildcard domain allows you to receive a randomly generated domain for your new applications. <br><br>For instance, if you set "https://example.com" as your wildcard domain, your applications will receive domains like "https://randomId.example.com".' /> <x-forms.input placeholder="https://example.com" id="wildcard_domain" label="Wildcard Domain"
helper='A wildcard domain allows you to receive a randomly generated domain for your new applications. <br><br>For instance, if you set "https://example.com" as your wildcard domain, your applications will receive domains like "https://randomId.example.com".' />
@endif @endif
</div> </div>
<div class="flex flex-col w-full gap-2 lg:flex-row"> <div class="flex flex-col w-full gap-2 lg:flex-row">
<x-forms.input type="password" id="server.ip" label="IP Address/Domain" helper="An IP Address (127.0.0.1) or domain (example.com)." required /> <x-forms.input type="password" id="server.ip" label="IP Address/Domain"
helper="An IP Address (127.0.0.1) or domain (example.com)." required />
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input id="server.user" label="User" required /> <x-forms.input id="server.user" label="User" required />
<x-forms.input type="number" id="server.port" label="Port" required /> <x-forms.input type="number" id="server.port" label="Port" required />
@@ -72,80 +77,103 @@
</div> </div>
<div class="w-64"> <div class="w-64">
@if ($server->isFunctional()) @if ($server->isFunctional())
@if (!$server->isLocalhost()) @if (!$server->isLocalhost())
<x-forms.checkbox instantSave id="server.settings.is_build_server" label="Use it as a build server?" /> <x-forms.checkbox instantSave id="server.settings.is_build_server"
<div class="flex items-center gap-1 pt-6"> label="Use it as a build server?" />
<h3 class="">Cloudflare Tunnels <div class="flex items-center gap-1 pt-6">
</h3> <h3 class="">Cloudflare Tunnels
<x-helper class="inline-flex" helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" /> </h3>
</div> <x-helper class="inline-flex"
@if ($server->settings->is_cloudflare_tunnel) helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
<x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" /> </div>
@else @if ($server->settings->is_cloudflare_tunnel)
<x-modal-input buttonTitle="Configure" title="Cloudflare Tunnels"> <x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" />
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" /> @else
</x-modal-input> <x-modal-input buttonTitle="Configure" title="Cloudflare Tunnels">
@endif <livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
@if (!$server->isBuildServer()) </x-modal-input>
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span></h3> @endif
<div class="pb-4">Read the docs <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>. @if (!$server->isBuildServer())
</div> <h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span></h3>
@if ($server->settings->is_swarm_worker) <div class="pb-4">Read the docs <a class='underline dark:text-white'
<x-forms.checkbox disabled instantSave type="checkbox" id="server.settings.is_swarm_manager" helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>." label="Is it a Swarm Manager?" /> href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>.
@else </div>
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_manager" helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>." label="Is it a Swarm Manager?" /> @if ($server->settings->is_swarm_worker)
@endif <x-forms.checkbox disabled instantSave type="checkbox"
id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
@if ($server->settings->is_swarm_manager) @if ($server->settings->is_swarm_manager)
<x-forms.checkbox disabled instantSave type="checkbox" id="server.settings.is_swarm_worker" helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>." label="Is it a Swarm Worker?" /> <x-forms.checkbox disabled instantSave type="checkbox"
id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@endif
@endif
@endif
@else @else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_worker" helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>." label="Is it a Swarm Worker?" /> <div class="flex items-center gap-1 pt-6">
@endif <h3 class="">Cloudflare Tunnels
@endif </h3>
@endif <x-helper class="inline-flex"
@else helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
<div class="flex items-center gap-1 pt-6"> </div>
<h3 class="">Cloudflare Tunnels @if ($server->settings->is_cloudflare_tunnel)
</h3> <x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" />
<x-helper class="inline-flex" helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" /> @else
</div> <x-modal-input buttonTitle="Configure" title="Cloudflare Tunnels">
@if ($server->settings->is_cloudflare_tunnel) <livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
<x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" /> </x-modal-input>
@else @endif
<x-modal-input buttonTitle="Configure" title="Cloudflare Tunnels">
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
</x-modal-input>
@endif
@endif @endif
</div> </div>
</div> </div>
@if ($server->isFunctional()) @if ($server->isFunctional())
<h3 class="pt-4">Settings</h3> <h3 class="pt-4">Settings</h3>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex flex-col flex-wrap gap-2 sm:flex-nowrap"> <div class="flex flex-col flex-wrap gap-2 sm:flex-nowrap">
<div class="w-64"> <div class="w-64">
<x-forms.checkbox helper="Enable force Docker Cleanup. This will cleanup build caches / unused images / etc." instantSave id="server.settings.force_docker_cleanup" label="Force Docker Cleanup" /> </div> <x-forms.checkbox
@if ($server->settings->force_docker_cleanup) helper="Enable force Docker Cleanup. This will cleanup build caches / unused images / etc."
<x-forms.input placeholder="*/10 * * * *" id="server.settings.docker_cleanup_frequency" label="Docker Cleanup Frequency" required helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every 10 minutes." /> instantSave id="server.settings.force_docker_cleanup" label="Force Docker Cleanup" />
@else </div>
<x-forms.input id="server.settings.docker_cleanup_threshold" label="Docker cleanup threshold (%)" required helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." /> @if ($server->settings->force_docker_cleanup)
@endif <x-forms.input placeholder="*/10 * * * *" id="server.settings.docker_cleanup_frequency"
label="Docker cleanup frequency" required
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every 10 minutes." />
@else
<x-forms.input id="server.settings.docker_cleanup_threshold"
label="Docker cleanup threshold (%)" required
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
@endif
</div>
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input id="server.settings.concurrent_builds" label="Number of concurrent builds" required
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
<x-forms.input id="server.settings.dynamic_timeout" label="Deployment timeout (seconds)" required
helper="You can define the maximum duration for a deployment to run before timing it out." />
</div>
</div> </div>
<div class="flex flex-wrap gap-2 sm:flex-nowrap"> <div class="flex items-center gap-2 pt-4 pb-2">
<x-forms.input id="server.settings.concurrent_builds" label="Number of concurrent builds" required helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." /> <h3>Sentinel</h3>
<x-forms.input id="server.settings.dynamic_timeout" label="Deployment timeout (seconds)" required helper="You can define the maximum duration for a deployment to run before timing it out." /> {{-- @if ($server->isSentinelEnabled()) --}}
{{-- <x-forms.button wire:click='restartSentinel'>Restart</x-forms.button> --}}
{{-- @endif --}}
</div> </div>
</div> <div>Metrics are disabled until a few bugs are fixed.</div>
<div class="flex items-center gap-2 pt-4 pb-2"> {{-- <div class="w-64">
<h3>Sentinel</h3>
{{-- @if ($server->isSentinelEnabled()) --}}
{{-- <x-forms.button wire:click='restartSentinel'>Restart</x-forms.button> --}}
{{-- @endif --}}
</div>
<div>Metrics are disabled until a few bugs are fixed.</div>
{{-- <div class="w-64">
<x-forms.checkbox instantSave id="server.settings.is_metrics_enabled" label="Enable Metrics" /> <x-forms.checkbox instantSave id="server.settings.is_metrics_enabled" label="Enable Metrics" />
</div> </div>
<div class="pt-4"> <div class="pt-4">