Feat: Manual cleanup button and unused volumes and network deletion
This commit is contained in:
		@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 = [
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    public function up()
 | 
			
		||||
    {
 | 
			
		||||
        Schema::table('server_settings', function (Blueprint $table) {
 | 
			
		||||
            $table->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');
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -183,26 +183,45 @@
 | 
			
		||||
 | 
			
		||||
        @if ($server->isFunctional())
 | 
			
		||||
            <h3 class="pt-4">Settings</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1">
 | 
			
		||||
            <div class="flex flex-col gap-4">
 | 
			
		||||
                <div class="flex flex-col gap-2">
 | 
			
		||||
                    <div class="flex flex-col flex-wrap gap-2 sm:flex-nowrap">
 | 
			
		||||
                    <div class="flex flex-wrap items-center gap-4">
 | 
			
		||||
                        <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>
 | 
			
		||||
                        @if ($server->settings->force_docker_cleanup)
 | 
			
		||||
                            <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 night at midnight." />
 | 
			
		||||
                        @else
 | 
			
		||||
                        <x-forms.button wire:click="manualCleanup">
 | 
			
		||||
                            Manually Trigger Cleanup
 | 
			
		||||
                        </x-forms.button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    @if ($server->settings->force_docker_cleanup)
 | 
			
		||||
                    <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 night at midnight." />
 | 
			
		||||
                    @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
 | 
			
		||||
                    @endif
 | 
			
		||||
                    <div x-data="{ open: false }" class="mt-4 max-w-md">
 | 
			
		||||
                        <button @click="open = !open" type="button" class="flex items-center justify-between w-full text-left text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100">
 | 
			
		||||
                            <span>Advanced Options</span>
 | 
			
		||||
                            <svg :class="{'rotate-180': open}" class="w-5 h-5 transition-transform duration-200" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
 | 
			
		||||
                                <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
 | 
			
		||||
                            </svg>
 | 
			
		||||
                        </button>
 | 
			
		||||
                        <div x-show="open" class="mt-2 space-y-2">
 | 
			
		||||
                            <p class="text-sm text-gray-600 dark:text-gray-400 mb-2"><strong>Only enable if you know what you are doing! As data cold be lost and functional issues might occur.</strong></p>
 | 
			
		||||
                            <x-forms.checkbox instantSave id="server.settings.delete_unused_volumes" label="Delete Unused Volumes"
 | 
			
		||||
                                helper="This will remove all unused Docker volumes during cleanup. <br>Volumes are not removed by default to prevent data loss as if you have stopped a container that has a volume mounted your data would be lost." />
 | 
			
		||||
                            <x-forms.checkbox instantSave id="server.settings.delete_unused_networks" label="Delete Unused Networks"
 | 
			
		||||
                                helper="This will remove all unused Docker networks during cleanup. <br>Networks are not removed by default to prevent functional issues as if you have stopped a container that has a network attached to it the network will not be removed and other containers might not be able to connect to it anymore." />
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-wrap gap-2 sm:flex-nowrap">
 | 
			
		||||
                
 | 
			
		||||
                <div class="flex flex-wrap gap-4 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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user