diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index dfcf9ee09..19122cbdd 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -1237,7 +1237,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
});
foreach ($runtime_environment_variables as $env) {
- $envs->push($env->key.'='.$env->real_value);
+ $envs->push($env->key.'='.$env->getResolvedValueWithServer($this->server));
}
// Check for PORT environment variable mismatch with ports_exposes
@@ -1303,7 +1303,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
});
foreach ($runtime_environment_variables_preview as $env) {
- $envs->push($env->key.'='.$env->real_value);
+ $envs->push($env->key.'='.$env->getResolvedValueWithServer($this->server));
}
// Add PORT if not exists, use the first port as default
if ($this->build_pack !== 'dockercompose') {
@@ -2324,14 +2324,16 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->env_nixpacks_args = collect([]);
if ($this->pull_request_id === 0) {
foreach ($this->application->nixpacks_environment_variables as $env) {
- if (! is_null($env->real_value) && $env->real_value !== '') {
- $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
+ $resolvedValue = $env->getResolvedValueWithServer($this->server);
+ if (! is_null($resolvedValue) && $resolvedValue !== '') {
+ $this->env_nixpacks_args->push("--env {$env->key}={$resolvedValue}");
}
}
} else {
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
- if (! is_null($env->real_value) && $env->real_value !== '') {
- $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
+ $resolvedValue = $env->getResolvedValueWithServer($this->server);
+ if (! is_null($resolvedValue) && $resolvedValue !== '') {
+ $this->env_nixpacks_args->push("--env {$env->key}={$resolvedValue}");
}
}
}
@@ -2467,8 +2469,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
->get();
foreach ($envs as $env) {
- if (! is_null($env->real_value)) {
- $this->env_args->put($env->key, $env->real_value);
+ $resolvedValue = $env->getResolvedValueWithServer($this->server);
+ if (! is_null($resolvedValue)) {
+ $this->env_args->put($env->key, $resolvedValue);
}
}
} else {
@@ -2478,8 +2481,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
->get();
foreach ($envs as $env) {
- if (! is_null($env->real_value)) {
- $this->env_args->put($env->key, $env->real_value);
+ $resolvedValue = $env->getResolvedValueWithServer($this->server);
+ if (! is_null($resolvedValue)) {
+ $this->env_args->put($env->key, $resolvedValue);
}
}
}
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
index fa65e8bd2..fdcb39270 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
@@ -67,6 +67,7 @@ class Add extends Component
'team' => [],
'project' => [],
'environment' => [],
+ 'server' => [],
];
// Early return if no team
@@ -122,6 +123,66 @@ class Add extends Component
}
}
+ // Get server variables
+ $serverUuid = data_get($this->parameters, 'server_uuid');
+ if ($serverUuid) {
+ // If we have a specific server_uuid, show variables for that server
+ $server = \App\Models\Server::where('team_id', $team->id)
+ ->where('uuid', $serverUuid)
+ ->first();
+
+ if ($server) {
+ try {
+ $this->authorize('view', $server);
+ $result['server'] = $server->environment_variables()
+ ->pluck('key')
+ ->toArray();
+ } catch (\Illuminate\Auth\Access\AuthorizationException $e) {
+ // User not authorized to view server variables
+ }
+ }
+ } else {
+ // For application environment variables, try to use the application's destination server
+ $applicationUuid = data_get($this->parameters, 'application_uuid');
+ if ($applicationUuid) {
+ $application = \App\Models\Application::whereRelation('environment.project.team', 'id', $team->id)
+ ->where('uuid', $applicationUuid)
+ ->with('destination.server')
+ ->first();
+
+ if ($application && $application->destination && $application->destination->server) {
+ try {
+ $this->authorize('view', $application->destination->server);
+ $result['server'] = $application->destination->server->environment_variables()
+ ->pluck('key')
+ ->toArray();
+ } catch (\Illuminate\Auth\Access\AuthorizationException $e) {
+ // User not authorized to view server variables
+ }
+ }
+ } else {
+ // For service environment variables, try to use the service's server
+ $serviceUuid = data_get($this->parameters, 'service_uuid');
+ if ($serviceUuid) {
+ $service = \App\Models\Service::whereRelation('environment.project.team', 'id', $team->id)
+ ->where('uuid', $serviceUuid)
+ ->with('server')
+ ->first();
+
+ if ($service && $service->server) {
+ try {
+ $this->authorize('view', $service->server);
+ $result['server'] = $service->server->environment_variables()
+ ->pluck('key')
+ ->toArray();
+ } catch (\Illuminate\Auth\Access\AuthorizationException $e) {
+ // User not authorized to view server variables
+ }
+ }
+ }
+ }
+ }
+
return $result;
}
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
index 2030f631e..ac7549f18 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
@@ -204,6 +204,7 @@ class Show extends Component
'team' => [],
'project' => [],
'environment' => [],
+ 'server' => [],
];
// Early return if no team
@@ -259,6 +260,66 @@ class Show extends Component
}
}
+ // Get server variables
+ $serverUuid = data_get($this->parameters, 'server_uuid');
+ if ($serverUuid) {
+ // If we have a specific server_uuid, show variables for that server
+ $server = \App\Models\Server::where('team_id', $team->id)
+ ->where('uuid', $serverUuid)
+ ->first();
+
+ if ($server) {
+ try {
+ $this->authorize('view', $server);
+ $result['server'] = $server->environment_variables()
+ ->pluck('key')
+ ->toArray();
+ } catch (\Illuminate\Auth\Access\AuthorizationException $e) {
+ // User not authorized to view server variables
+ }
+ }
+ } else {
+ // For application environment variables, try to use the application's destination server
+ $applicationUuid = data_get($this->parameters, 'application_uuid');
+ if ($applicationUuid) {
+ $application = \App\Models\Application::whereRelation('environment.project.team', 'id', $team->id)
+ ->where('uuid', $applicationUuid)
+ ->with('destination.server')
+ ->first();
+
+ if ($application && $application->destination && $application->destination->server) {
+ try {
+ $this->authorize('view', $application->destination->server);
+ $result['server'] = $application->destination->server->environment_variables()
+ ->pluck('key')
+ ->toArray();
+ } catch (\Illuminate\Auth\Access\AuthorizationException $e) {
+ // User not authorized to view server variables
+ }
+ }
+ } else {
+ // For service environment variables, try to use the service's server
+ $serviceUuid = data_get($this->parameters, 'service_uuid');
+ if ($serviceUuid) {
+ $service = \App\Models\Service::whereRelation('environment.project.team', 'id', $team->id)
+ ->where('uuid', $serviceUuid)
+ ->with('server')
+ ->first();
+
+ if ($service && $service->server) {
+ try {
+ $this->authorize('view', $service->server);
+ $result['server'] = $service->server->environment_variables()
+ ->pluck('key')
+ ->toArray();
+ } catch (\Illuminate\Auth\Access\AuthorizationException $e) {
+ // User not authorized to view server variables
+ }
+ }
+ }
+ }
+ }
+
return $result;
}
diff --git a/app/Livewire/SharedVariables/Server/Index.php b/app/Livewire/SharedVariables/Server/Index.php
new file mode 100644
index 000000000..cd10e510a
--- /dev/null
+++ b/app/Livewire/SharedVariables/Server/Index.php
@@ -0,0 +1,22 @@
+servers = Server::ownedByCurrentTeamCached();
+ }
+
+ public function render()
+ {
+ return view('livewire.shared-variables.server.index');
+ }
+}
diff --git a/app/Livewire/SharedVariables/Server/Show.php b/app/Livewire/SharedVariables/Server/Show.php
new file mode 100644
index 000000000..6078ef36f
--- /dev/null
+++ b/app/Livewire/SharedVariables/Server/Show.php
@@ -0,0 +1,184 @@
+ 'refreshEnvs', 'saveKey' => 'saveKey', 'environmentVariableDeleted' => 'refreshEnvs'];
+
+ public function saveKey($data)
+ {
+ try {
+ $this->authorize('update', $this->server);
+
+ if (in_array($data['key'], ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])) {
+ throw new \Exception('Cannot create predefined variable.');
+ }
+
+ $found = $this->server->environment_variables()->where('key', $data['key'])->first();
+ if ($found) {
+ throw new \Exception('Variable already exists.');
+ }
+ $this->server->environment_variables()->create([
+ 'key' => $data['key'],
+ 'value' => $data['value'],
+ 'is_multiline' => $data['is_multiline'],
+ 'is_literal' => $data['is_literal'],
+ 'type' => 'server',
+ 'team_id' => currentTeam()->id,
+ ]);
+ $this->server->refresh();
+ $this->getDevView();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function mount()
+ {
+ $serverUuid = request()->route('server_uuid');
+ $teamId = currentTeam()->id;
+ $server = Server::where('team_id', $teamId)->where('uuid', $serverUuid)->first();
+ if (! $server) {
+ return redirect()->route('dashboard');
+ }
+ $this->authorize('view', $server);
+ $this->server = $server;
+ $this->getDevView();
+ }
+
+ public function switch()
+ {
+ $this->authorize('view', $this->server);
+ $this->view = $this->view === 'normal' ? 'dev' : 'normal';
+ $this->getDevView();
+ }
+
+ public function getDevView()
+ {
+ $this->variables = $this->formatEnvironmentVariables($this->server->environment_variables->whereNotIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])->sortBy('key'));
+ }
+
+ private function formatEnvironmentVariables($variables)
+ {
+ return $variables->map(function ($item) {
+ if ($item->is_shown_once) {
+ return "$item->key=(Locked Secret, delete and add again to change)";
+ }
+ if ($item->is_multiline) {
+ return "$item->key=(Multiline environment variable, edit in normal view)";
+ }
+
+ return "$item->key=$item->value";
+ })->join("\n");
+ }
+
+ public function submit()
+ {
+ try {
+ $this->authorize('update', $this->server);
+ $this->handleBulkSubmit();
+ $this->getDevView();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ } finally {
+ $this->refreshEnvs();
+ }
+ }
+
+ private function handleBulkSubmit()
+ {
+ $variables = parseEnvFormatToArray($this->variables);
+
+ $changesMade = DB::transaction(function () use ($variables) {
+ // Delete removed variables
+ $deletedCount = $this->deleteRemovedVariables($variables);
+
+ // Update or create variables
+ $updatedCount = $this->updateOrCreateVariables($variables);
+
+ return $deletedCount > 0 || $updatedCount > 0;
+ });
+
+ if ($changesMade) {
+ $this->dispatch('success', 'Environment variables updated.');
+ }
+ }
+
+ private function deleteRemovedVariables($variables)
+ {
+ $variablesToDelete = $this->server->environment_variables()
+ ->whereNotIn('key', array_keys($variables))
+ ->whereNotIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
+ ->get();
+
+ if ($variablesToDelete->isEmpty()) {
+ return 0;
+ }
+
+ $this->server->environment_variables()
+ ->whereNotIn('key', array_keys($variables))
+ ->whereNotIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
+ ->delete();
+
+ return $variablesToDelete->count();
+ }
+
+ private function updateOrCreateVariables($variables)
+ {
+ $count = 0;
+ foreach ($variables as $key => $value) {
+ // Skip predefined variables
+ if (in_array($key, ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])) {
+ continue;
+ }
+ $found = $this->server->environment_variables()->where('key', $key)->first();
+
+ if ($found) {
+ if (! $found->is_shown_once && ! $found->is_multiline) {
+ if ($found->value !== $value) {
+ $found->value = $value;
+ $found->save();
+ $count++;
+ }
+ }
+ } else {
+ $this->server->environment_variables()->create([
+ 'key' => $key,
+ 'value' => $value,
+ 'is_multiline' => false,
+ 'is_literal' => false,
+ 'type' => 'server',
+ 'team_id' => currentTeam()->id,
+ ]);
+ $count++;
+ }
+ }
+
+ return $count;
+ }
+
+ public function refreshEnvs()
+ {
+ $this->server->refresh();
+ $this->getDevView();
+ }
+
+ public function render()
+ {
+ return view('livewire.shared-variables.server.show');
+ }
+}
diff --git a/app/Models/Environment.php b/app/Models/Environment.php
index c2ad9d2cb..38138da1e 100644
--- a/app/Models/Environment.php
+++ b/app/Models/Environment.php
@@ -56,7 +56,7 @@ class Environment extends BaseModel
public function environment_variables()
{
- return $this->hasMany(SharedEnvironmentVariable::class);
+ return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'environment');
}
public function applications()
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index 4f1e277e4..4c7a0765f 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -122,6 +122,17 @@ class EnvironmentVariable extends BaseModel
return null;
}
+ // Load relationships needed for shared variable resolution
+ if (! $resource->relationLoaded('environment')) {
+ $resource->load('environment');
+ }
+ if (! $resource->relationLoaded('server') && method_exists($resource, 'server')) {
+ $resource->load('server');
+ }
+ if (! $resource->relationLoaded('destination') && method_exists($resource, 'destination')) {
+ $resource->load('destination.server');
+ }
+
$real_value = $this->get_real_environment_variables($this->value, $resource);
// Skip escaping for valid JSON objects/arrays to prevent quote corruption (see #6160)
@@ -187,7 +198,43 @@ class EnvironmentVariable extends BaseModel
);
}
- private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
+ public function get_real_environment_variables_with_server(?string $environment_variable = null, $resource = null, $server = null)
+ {
+ return $this->get_real_environment_variables_internal($environment_variable, $resource, $server);
+ }
+
+ public function getResolvedValueWithServer($server = null)
+ {
+ if (! $this->relationLoaded('resourceable')) {
+ $this->load('resourceable');
+ }
+ $resource = $this->resourceable;
+ if (! $resource) {
+ return null;
+ }
+
+ // Load relationships needed for shared variable resolution
+ if (! $resource->relationLoaded('environment')) {
+ $resource->load('environment');
+ }
+ if (! $resource->relationLoaded('server') && method_exists($resource, 'server')) {
+ $resource->load('server');
+ }
+ if (! $resource->relationLoaded('destination') && method_exists($resource, 'destination')) {
+ $resource->load('destination.server');
+ }
+
+ $real_value = $this->get_real_environment_variables_internal($this->value, $resource, $server);
+ if ($this->is_literal || $this->is_multiline) {
+ $real_value = '\''.$real_value.'\'';
+ } else {
+ $real_value = escapeEnvVariables($real_value);
+ }
+
+ return $real_value;
+ }
+
+ private function get_real_environment_variables_internal(?string $environment_variable = null, $resource = null, $serverOverride = null)
{
if ((is_null($environment_variable) && $environment_variable === '') || is_null($resource)) {
return null;
@@ -209,6 +256,17 @@ class EnvironmentVariable extends BaseModel
$id = $resource->environment->project->id;
} elseif ($type->value() === 'team') {
$id = $resource->team()->id;
+ } elseif ($type->value() === 'server') {
+ // Use server override if provided (for deployment context), otherwise use resource's server
+ if ($serverOverride) {
+ $id = $serverOverride->id;
+ } elseif (isset($resource->server) && $resource->server) {
+ $id = $resource->server->id;
+ } elseif (isset($resource->destination) && $resource->destination && isset($resource->destination->server)) {
+ $id = $resource->destination->server->id;
+ } else {
+ $id = null;
+ }
}
if (is_null($id)) {
continue;
@@ -222,6 +280,11 @@ class EnvironmentVariable extends BaseModel
return str($environment_variable)->value();
}
+ private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
+ {
+ return $this->get_real_environment_variables_internal($environment_variable, $resource);
+ }
+
private function get_environment_variables(?string $environment_variable = null): ?string
{
if (! $environment_variable) {
diff --git a/app/Models/Project.php b/app/Models/Project.php
index 181951f14..d926bf00c 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -67,7 +67,7 @@ class Project extends BaseModel
public function environment_variables()
{
- return $this->hasMany(SharedEnvironmentVariable::class);
+ return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'project');
}
public function environments()
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 49d9c3289..0d4d199c0 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -178,6 +178,24 @@ class Server extends BaseModel
if (! isset($server->proxy->redirect_enabled)) {
$server->proxy->redirect_enabled = true;
}
+
+ // Create predefined server shared variables
+ SharedEnvironmentVariable::create([
+ 'key' => 'COOLIFY_SERVER_UUID',
+ 'value' => $server->uuid,
+ 'type' => 'server',
+ 'server_id' => $server->id,
+ 'team_id' => $server->team_id,
+ 'is_literal' => true,
+ ]);
+ SharedEnvironmentVariable::create([
+ 'key' => 'COOLIFY_SERVER_NAME',
+ 'value' => $server->name,
+ 'type' => 'server',
+ 'server_id' => $server->id,
+ 'team_id' => $server->team_id,
+ 'is_literal' => true,
+ ]);
});
static::retrieved(function ($server) {
if (! isset($server->proxy->redirect_enabled)) {
@@ -1003,6 +1021,11 @@ $schema://$host {
return $this->belongsTo(Team::class);
}
+ public function environment_variables()
+ {
+ return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'server');
+ }
+
public function isProxyShouldRun()
{
// TODO: Do we need "|| $this->proxy->force_stop" here?
diff --git a/app/Models/SharedEnvironmentVariable.php b/app/Models/SharedEnvironmentVariable.php
index 7956f006a..7b68bbac9 100644
--- a/app/Models/SharedEnvironmentVariable.php
+++ b/app/Models/SharedEnvironmentVariable.php
@@ -27,4 +27,9 @@ class SharedEnvironmentVariable extends Model
{
return $this->belongsTo(Environment::class);
}
+
+ public function server()
+ {
+ return $this->belongsTo(Server::class);
+ }
}
diff --git a/app/Models/Team.php b/app/Models/Team.php
index e32526169..23f933350 100644
--- a/app/Models/Team.php
+++ b/app/Models/Team.php
@@ -215,7 +215,7 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
public function environment_variables()
{
- return $this->hasMany(SharedEnvironmentVariable::class)->whereNull('project_id')->whereNull('environment_id');
+ return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'team');
}
public function members()
diff --git a/app/View/Components/Forms/EnvVarInput.php b/app/View/Components/Forms/EnvVarInput.php
index 4a98e4a51..faef64a36 100644
--- a/app/View/Components/Forms/EnvVarInput.php
+++ b/app/View/Components/Forms/EnvVarInput.php
@@ -38,6 +38,7 @@ class EnvVarInput extends Component
public array $availableVars = [],
public ?string $projectUuid = null,
public ?string $environmentUuid = null,
+ public ?string $serverUuid = null,
) {
// Handle authorization-based disabling
if ($this->canGate && $this->canResource && $this->autoDisable) {
@@ -86,6 +87,9 @@ class EnvVarInput extends Component
'environment_uuid' => $this->environmentUuid,
])
: route('shared-variables.environment.index'),
+ 'server' => $this->serverUuid
+ ? route('shared-variables.server.show', ['server_uuid' => $this->serverUuid])
+ : route('shared-variables.server.index'),
'default' => route('shared-variables.index'),
];
diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php
index 30ca36f2e..bae2573de 100644
--- a/bootstrap/helpers/constants.php
+++ b/bootstrap/helpers/constants.php
@@ -81,4 +81,4 @@ const NEEDS_TO_DISABLE_GZIP = [
const NEEDS_TO_DISABLE_STRIPPREFIX = [
'appwrite' => ['appwrite', 'appwrite-console', 'appwrite-realtime'],
];
-const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment'];
+const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment', 'server'];
diff --git a/database/migrations/2025_12_24_095507_add_server_to_shared_environment_variables_table.php b/database/migrations/2025_12_24_095507_add_server_to_shared_environment_variables_table.php
new file mode 100644
index 000000000..0207ed955
--- /dev/null
+++ b/database/migrations/2025_12_24_095507_add_server_to_shared_environment_variables_table.php
@@ -0,0 +1,35 @@
+foreignId('server_id')->nullable()->constrained()->onDelete('cascade');
+ $table->unique(['key', 'server_id', 'team_id']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('shared_environment_variables', function (Blueprint $table) {
+ $table->dropUnique(['key', 'server_id', 'team_id']);
+ $table->dropForeign(['server_id']);
+ $table->dropColumn('server_id');
+ });
+ DB::statement("ALTER TABLE shared_environment_variables DROP CONSTRAINT shared_environment_variables_type_check");
+ DB::statement("ALTER TABLE shared_environment_variables ADD CONSTRAINT shared_environment_variables_type_check CHECK (type IN ('team', 'project', 'environment'))");
+ }
+};
diff --git a/database/migrations/2025_12_24_133707_add_predefined_server_variables_to_existing_servers.php b/database/migrations/2025_12_24_133707_add_predefined_server_variables_to_existing_servers.php
new file mode 100644
index 000000000..c67987e67
--- /dev/null
+++ b/database/migrations/2025_12_24_133707_add_predefined_server_variables_to_existing_servers.php
@@ -0,0 +1,56 @@
+chunk(100, function ($servers) {
+ foreach ($servers as $server) {
+ $existingKeys = SharedEnvironmentVariable::where('type', 'server')
+ ->where('server_id', $server->id)
+ ->whereIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
+ ->pluck('key')
+ ->toArray();
+
+ if (! in_array('COOLIFY_SERVER_UUID', $existingKeys)) {
+ SharedEnvironmentVariable::create([
+ 'key' => 'COOLIFY_SERVER_UUID',
+ 'value' => $server->uuid,
+ 'type' => 'server',
+ 'server_id' => $server->id,
+ 'team_id' => $server->team_id,
+ 'is_literal' => true,
+ ]);
+ }
+
+ if (! in_array('COOLIFY_SERVER_NAME', $existingKeys)) {
+ SharedEnvironmentVariable::create([
+ 'key' => 'COOLIFY_SERVER_NAME',
+ 'value' => $server->name,
+ 'type' => 'server',
+ 'server_id' => $server->id,
+ 'team_id' => $server->team_id,
+ 'is_literal' => true,
+ ]);
+ }
+ }
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ SharedEnvironmentVariable::where('type', 'server')
+ ->whereIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
+ ->delete();
+ }
+};
diff --git a/database/seeders/SharedEnvironmentVariableSeeder.php b/database/seeders/SharedEnvironmentVariableSeeder.php
index 54643fe3b..7a17fbd10 100644
--- a/database/seeders/SharedEnvironmentVariableSeeder.php
+++ b/database/seeders/SharedEnvironmentVariableSeeder.php
@@ -2,6 +2,7 @@
namespace Database\Seeders;
+use App\Models\Server;
use App\Models\SharedEnvironmentVariable;
use Illuminate\Database\Seeder;
@@ -32,5 +33,29 @@ class SharedEnvironmentVariableSeeder extends Seeder
'project_id' => 1,
'team_id' => 0,
]);
+
+ // Add predefined server variables to all existing servers
+ $servers = \App\Models\Server::all();
+ foreach ($servers as $server) {
+ SharedEnvironmentVariable::firstOrCreate([
+ 'key' => 'COOLIFY_SERVER_UUID',
+ 'type' => 'server',
+ 'server_id' => $server->id,
+ 'team_id' => $server->team_id,
+ ], [
+ 'value' => $server->uuid,
+ 'is_literal' => true,
+ ]);
+
+ SharedEnvironmentVariable::firstOrCreate([
+ 'key' => 'COOLIFY_SERVER_NAME',
+ 'type' => 'server',
+ 'server_id' => $server->id,
+ 'team_id' => $server->team_id,
+ ], [
+ 'value' => $server->name,
+ 'is_literal' => true,
+ ]);
+ }
}
}
diff --git a/resources/views/components/forms/env-var-input.blade.php b/resources/views/components/forms/env-var-input.blade.php
index 2466a57f9..61e308a83 100644
--- a/resources/views/components/forms/env-var-input.blade.php
+++ b/resources/views/components/forms/env-var-input.blade.php
@@ -17,8 +17,15 @@
selectedIndex: 0,
cursorPosition: 0,
currentScope: null,
- availableScopes: ['team', 'project', 'environment'],
availableVars: @js($availableVars),
+ get availableScopes() {
+ // Only include scopes that have at least one variable
+ const allScopes = ['team', 'project', 'environment', 'server'];
+ return allScopes.filter(scope => {
+ const vars = this.availableVars[scope];
+ return vars && vars.length > 0;
+ });
+ },
scopeUrls: @js($scopeUrls),
handleInput() {
@@ -54,6 +61,11 @@
if (content === '') {
this.currentScope = null;
+ // Only show dropdown if there are available scopes with variables
+ if (this.availableScopes.length === 0) {
+ this.showDropdown = false;
+ return;
+ }
this.suggestions = this.availableScopes.map(scope => ({
type: 'scope',
value: scope,
diff --git a/resources/views/livewire/project/shared/environment-variable/add.blade.php b/resources/views/livewire/project/shared/environment-variable/add.blade.php
index 9bc4f06a3..daf808c5e 100644
--- a/resources/views/livewire/project/shared/environment-variable/add.blade.php
+++ b/resources/views/livewire/project/shared/environment-variable/add.blade.php
@@ -6,7 +6,8 @@