Root cause
Other / insufficient public mechanism detail introduced
fix(settings): fix 404 on /settings for root user on cloud instance
How AI contributed: Direct introductionPublished Jul 6, 2026 in
coollabsio/coolifyAll 4 findings in this repository
The Claude-written change made the updates settings page tolerate a missing server row by calling Server::findOrFail(0) only on self-hosted installs, which removed the 404 that cloud users previously hit. That error had been the only barrier in front of the page, because the Livewire component never checked for instance administrator rights. Any signed-in cloud user could then open the updates page and change auto-update settings. The fix adds an instance administrator check that redirects everyone else to the dashboard.
The updates component in app/Livewire/Settings/Updates.php depended on a server lookup that threw on cloud installs, and that 404 was mistaken for access control. Narrowing the lookup to self-hosted installs left mount() without any isInstanceAdmin() call, so the page stayed reachable for every authenticated user until the fix added the redirect.
Root cause
fix(settings): fix 404 on /settings for root user on cloud instance
Fix
fix(settings): require instance admin authorization for updates page
Fix by Andras Bacsai · no AI marker found
Case facts
@@ -12,7 +12,7 @@ class Updates extends Component { public InstanceSettings $settings; - public Server $server;+ public ?Server $server = null; #[Validate('string')] public string $auto_update_frequency;@@ -25,7 +25,9 @@ class Updates extends Component public function mount() {- $this->server = Server::findOrFail(0);+ if (! isCloud()) {+ $this->server = Server::findOrFail(0);+ } $this->settings = instanceSettings(); $this->auto_update_frequency = $this->settings->auto_update_frequency;@@ -76,7 +78,9 @@ public function submit() } $this->instantSave();- $this->server->setupDynamicProxyConfiguration();+ if ($this->server) {+ $this->server->setupDynamicProxyConfiguration();+ } } catch (\Exception $e) { return handleError($e, $this); }@@ -12,7 +12,7 @@ class Index extends Component { public InstanceSettings $settings; - public Server $server;+ public ?Server $server = null; #[Validate('nullable|string|max:255')] public ?string $fqdn = null;@@ -57,7 +57,9 @@ public function mount() return redirect()->route('dashboard'); } $this->settings = instanceSettings();- $this->server = Server::findOrFail(0);+ if (! isCloud()) {+ $this->server = Server::findOrFail(0);+ } $this->fqdn = $this->settings->fqdn; $this->public_port_min = $this->settings->public_port_min; $this->public_port_max = $this->settings->public_port_max;@@ -121,7 +123,7 @@ public function submit() } $this->validate(); - if ($this->settings->is_dns_validation_enabled && $this->fqdn) {+ if ($this->settings->is_dns_validation_enabled && $this->fqdn && $this->server) { if (! validateDNSEntry($this->fqdn, $this->server)) { $this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help."); $error_show = true;@@ -145,7 +147,9 @@ public function submit() $this->instantSave(isSave: false); $this->settings->save();- $this->server->setupDynamicProxyConfiguration();+ if ($this->server) {+ $this->server->setupDynamicProxyConfiguration();+ } if (! $error_show) { $this->dispatch('success', 'Instance settings updated successfully!'); }@@ -163,6 +167,12 @@ public function buildHelperImage() return; } + if (! $this->server) {+ $this->dispatch('error', 'Server not available.');++ return;+ }+ $version = $this->dev_helper_version ?: config('constants.coolify.helper_version'); if (empty($version)) { $this->dispatch('error', 'Please specify a version to build.');@@ -10,8 +10,7 @@ class Advanced extends Component {- #[Validate('required')]- public Server $server;+ public ?Server $server = null; public InstanceSettings $settings; @@ -44,7 +43,6 @@ class Advanced extends Component public function rules() { return [- 'server' => 'required', 'is_registration_enabled' => 'boolean', 'do_not_track' => 'boolean', 'is_dns_validation_enabled' => 'boolean',@@ -62,7 +60,9 @@ public function mount() if (! isInstanceAdmin()) { return redirect()->route('dashboard'); }- $this->server = Server::findOrFail(0);+ if (! isCloud()) {+ $this->server = Server::findOrFail(0);+ } $this->settings = instanceSettings(); $this->custom_dns_servers = $this->settings->custom_dns_servers; $this->allowed_ips = $this->settings->allowed_ips;@@ -25,6 +25,9 @@ class Updates extends Component public function mount() {+ if (! isInstanceAdmin()) {+ return redirect()->route('dashboard');+ } if (! isCloud()) { $this->server = Server::findOrFail(0); }@@ -0,0 +1,41 @@+<?php++use App\Livewire\Settings\Updates;+use App\Models\InstanceSettings;+use App\Models\Server;+use App\Models\Team;+use App\Models\User;+use Illuminate\Foundation\Testing\RefreshDatabase;+use Illuminate\Support\Once;+use Livewire\Livewire;++uses(RefreshDatabase::class);++test('non-admin user is redirected from settings updates page', function () {+ $team = Team::factory()->create();+ $user = User::factory()->create();+ $team->members()->attach($user->id, ['role' => 'member']);++ $this->actingAs($user);+ session(['currentTeam' => ['id' => $team->id]]);++ Livewire::test(Updates::class)+ ->assertRedirect(route('dashboard'));+});++test('instance admin can access settings updates page', function () {+ $rootTeam = Team::find(0) ?? Team::factory()->create(['id' => 0]);+ Server::factory()->create(['id' => 0, 'team_id' => $rootTeam->id]);+ InstanceSettings::create(['id' => 0]);+ Once::flush();++ $user = User::factory()->create();+ $rootTeam->members()->attach($user->id, ['role' => 'admin']);++ $this->actingAs($user);+ session(['currentTeam' => ['id' => $rootTeam->id]]);++ Livewire::test(Updates::class)+ ->assertOk()+ ->assertNoRedirect();+});AI-assisted change b20a6c4c0b18f7cfb1158a2dda4585ec2a58e8e44867dcddfebcb3d50ee3419d · Fix 874a21c21622fc19086998c22a2bd79e250a784a6439a0a096a03f405ddd64d5