diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index 1034c13d6..34126deeb 100644 --- a/app/Actions/Server/CleanupDocker.php +++ b/app/Actions/Server/CleanupDocker.php @@ -12,28 +12,29 @@ class CleanupDocker public function handle(Server $server) { + $settings = InstanceSettings::get(); + $helperImageVersion = data_get($settings, 'helper_version'); + $helperImage = config('coolify.helper_image'); + $helperImageWithVersion = "$helperImage:$helperImageVersion"; - $commands = $this->getCommands(); + $commands = [ + 'docker container prune -f --filter "label=coolify.managed=true"', + 'docker image prune -af --filter "label!=coolify.managed=true"', + 'docker builder prune -af', + "docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print \$3}' | xargs -r docker rmi", + ]; + + $serverSettings = $server->settings; + if ($serverSettings->delete_unused_volumes) { + $commands[] = 'docker volume prune -af'; + } + + if ($serverSettings->delete_unused_networks) { + $commands[] = 'docker network prune -f'; + } foreach ($commands as $command) { instant_remote_process([$command], $server, false); } } - - private function getCommands(): array - { - $settings = InstanceSettings::get(); - $helperImageVersion = data_get($settings, 'helper_version'); - $helperImage = config('coolify.helper_image'); - $helperImageWithVersion = config('coolify.helper_image').':'.$helperImageVersion; - - $commonCommands = [ - 'docker container prune -f --filter "label=coolify.managed=true"', - 'docker image prune -af --filter "label!=coolify.managed=true"', - 'docker builder prune -af', - "docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi", - ]; - - return $commonCommands; - } } diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index f95cd2920..4874e9670 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -24,7 +24,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue public ?string $usageBefore = null; - public function __construct(public Server $server) {} + public function __construct(public Server $server, public bool $manualCleanup = false) {} public function middleware(): array { @@ -42,10 +42,10 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue if (! $this->server->isFunctional()) { return; } - if ($this->server->settings->force_docker_cleanup) { - Log::info('DockerCleanupJob force cleanup on '.$this->server->name); - CleanupDocker::run(server: $this->server); + if ($this->manualCleanup || $this->server->settings->force_docker_cleanup) { + Log::info('DockerCleanupJob ' . ($this->manualCleanup ? 'manual' : 'force') . ' cleanup on ' . $this->server->name); + CleanupDocker::run(server: $this->server); return; } diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php index 3b3747a81..a5786038c 100644 --- a/app/Livewire/Server/Form.php +++ b/app/Livewire/Server/Form.php @@ -7,6 +7,7 @@ use App\Actions\Server\StopSentinel; use App\Jobs\PullSentinelImageJob; use App\Models\Server; use Livewire\Component; +use App\Jobs\DockerCleanupJob; class Form extends Component { @@ -24,6 +25,9 @@ class Form extends Component public $timezones; + public $delete_unused_volumes = false; + public $delete_unused_networks = false; + protected $listeners = [ 'serverInstalled', 'refreshServerShow' => 'serverInstalled', @@ -53,6 +57,8 @@ class Form extends Component 'server.settings.force_docker_cleanup' => 'required|boolean', 'server.settings.docker_cleanup_frequency' => 'required_if:server.settings.force_docker_cleanup,true|string', 'server.settings.docker_cleanup_threshold' => 'required_if:server.settings.force_docker_cleanup,false|integer|min:1|max:100', + 'server.settings.delete_unused_volumes' => 'boolean', + 'server.settings.delete_unused_networks' => 'boolean', ]; protected $validationAttributes = [ @@ -74,6 +80,8 @@ class Form extends Component 'server.settings.metrics_history_days' => 'Metrics History', 'server.settings.is_server_api_enabled' => 'Server API', 'server.settings.server_timezone' => 'Server Timezone', + 'server.settings.delete_unused_volumes' => 'Delete Unused Volumes', + 'server.settings.delete_unused_networks' => 'Delete Unused Networks', ]; public function mount(Server $server) @@ -83,6 +91,8 @@ class Form extends Component $this->wildcard_domain = $this->server->settings->wildcard_domain; $this->server->settings->docker_cleanup_threshold = $this->server->settings->docker_cleanup_threshold; $this->server->settings->docker_cleanup_frequency = $this->server->settings->docker_cleanup_frequency; + $this->server->settings->delete_unused_volumes = $server->settings->delete_unused_volumes; + $this->server->settings->delete_unused_networks = $server->settings->delete_unused_networks; } public function updated($field) @@ -126,6 +136,7 @@ class Form extends Component try { refresh_server_connection($this->server->privateKey); $this->validateServer(false); + $this->server->settings->save(); $this->server->save(); $this->dispatch('success', 'Server updated.'); @@ -143,6 +154,7 @@ class Form extends Component ray('Sentinel is not enabled'); StopSentinel::dispatch($this->server); } + $this->server->settings->save(); // $this->checkPortForServerApi(); } catch (\Throwable $e) { @@ -223,9 +235,9 @@ class Form extends Component $this->server->settings->server_timezone = $newTimezone; $this->server->settings->save(); } - $this->server->settings->save(); $this->server->save(); + $this->dispatch('success', 'Server updated.'); } catch (\Throwable $e) { return handleError($e, $this); @@ -238,4 +250,14 @@ class Form extends Component $this->server->settings->save(); $this->dispatch('success', 'Server timezone updated.'); } + + public function manualCleanup() + { + try { + DockerCleanupJob::dispatch($this->server, true); + $this->dispatch('success', 'Manual cleanup job started. Depending on the amount of data, this might take a while.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 90e6ade14..8a30b1a4d 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -37,6 +37,8 @@ use Symfony\Component\Yaml\Yaml; 'validation_logs' => ['type' => 'string'], 'log_drain_notification_sent' => ['type' => 'boolean'], 'swarm_cluster' => ['type' => 'string'], + 'delete_unused_volumes' => ['type' => 'boolean'], + 'delete_unused_networks' => ['type' => 'boolean'], ] )] @@ -106,6 +108,8 @@ class Server extends BaseModel 'proxy' => SchemalessAttributes::class, 'logdrain_axiom_api_key' => 'encrypted', 'logdrain_newrelic_license_key' => 'encrypted', + 'delete_unused_volumes' => 'boolean', + 'delete_unused_networks' => 'boolean', ]; protected $schemalessAttributes = [ diff --git a/database/migrations/2024_09_22_165240_add_advanced_options_to_cleanup_options_to_servers_settings_table.php b/database/migrations/2024_09_22_165240_add_advanced_options_to_cleanup_options_to_servers_settings_table.php new file mode 100644 index 000000000..b3c58afe3 --- /dev/null +++ b/database/migrations/2024_09_22_165240_add_advanced_options_to_cleanup_options_to_servers_settings_table.php @@ -0,0 +1,24 @@ +boolean('delete_unused_volumes')->default(false); + $table->boolean('delete_unused_networks')->default(false); + }); + } + + public function down() + { + Schema::table('server_settings', function (Blueprint $table) { + $table->dropColumn('delete_unused_volumes'); + $table->dropColumn('delete_unused_networks'); + }); + } +}; diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index 03d2cfeb4..a8160c675 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -183,26 +183,45 @@ @if ($server->isFunctional())

Settings

-
+
-
+
- @if ($server->settings->force_docker_cleanup) - - @else + + Manually Trigger Cleanup + +
+ @if ($server->settings->force_docker_cleanup) + + @else - @endif + @endif +
+ +
+

Only enable if you know what you are doing! As data cold be lost and functional issues might occur.

+ + +
-
+ +