Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e8d9852
feat: shared server environment variables
ShadowArcanist Dec 24, 2025
81009c2
fix: server env shows not found on application variables input field …
ShadowArcanist Dec 24, 2025
5ed308d
feat: predefined server variables (COOLIFY_SERVER_NAME, COOLIFY_SERVE…
ShadowArcanist Dec 24, 2025
09e14d2
fix: server env not showing for services
ShadowArcanist Dec 24, 2025
82b19e5
fix: predefined server env were not generated for existing servers
ShadowArcanist Dec 24, 2025
e3df380
fix: change value cast to encrypted for shared environment variables
andrasbacsai Jan 2, 2026
99d22ae
fix: filter available scopes based on existing variables in env var i…
andrasbacsai Jan 2, 2026
510fb22
fix: add 'is_literal' flag to shared environment variables for servers
andrasbacsai Jan 2, 2026
9cd8dff
fix: remove redundant sort call in environment variables display
andrasbacsai Jan 2, 2026
5661c13
fix: ensure authorization check for server view in mount method
andrasbacsai Jan 2, 2026
54c2871
fix: streamline migration for adding predefined server variables to e…
andrasbacsai Jan 2, 2026
e412b57
Merge branch 'next' into shadow/add-shared-server-env
ShadowArcanist Jan 14, 2026
cb97a18
Merge remote-tracking branch 'origin/next' into pr-7764-shadow/add-sh…
andrasbacsai Mar 31, 2026
30751a6
fix(deployment): resolve shared env vars using main server
andrasbacsai Mar 31, 2026
9c646b0
Merge remote-tracking branch 'origin/next' into pr-7764-shadow/add-sh…
andrasbacsai Mar 31, 2026
4f6e1f7
style(navbar): use tracking-tight instead of tracking-wide for logo
andrasbacsai Mar 31, 2026
466eb85
refactor(models): extract defaultStandaloneDockerAttributes method on…
andrasbacsai Mar 31, 2026
acb716c
fix(shared-variables): support direct mount params and comment field …
andrasbacsai Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 29 additions & 23 deletions app/Jobs/ApplicationDeploymentJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -1282,7 +1282,7 @@ private function generate_runtime_environment_variables()
});

foreach ($runtime_environment_variables as $env) {
$envs->push($env->key.'='.$env->real_value);
$envs->push($env->key.'='.$env->getResolvedValueWithServer($this->mainServer));
}

// Check for PORT environment variable mismatch with ports_exposes
Expand Down Expand Up @@ -1348,7 +1348,7 @@ private function generate_runtime_environment_variables()
});

foreach ($runtime_environment_variables_preview as $env) {
$envs->push($env->key.'='.$env->real_value);
$envs->push($env->key.'='.$env->getResolvedValueWithServer($this->mainServer));
}

// Fall back to production env vars for keys not overridden by preview vars,
Expand All @@ -1362,7 +1362,7 @@ private function generate_runtime_environment_variables()
return $env->is_runtime && ! in_array($env->key, $previewKeys);
});
foreach ($fallback_production_vars as $env) {
$envs->push($env->key.'='.$env->real_value);
$envs->push($env->key.'='.$env->getResolvedValueWithServer($this->mainServer));
}
}

Expand Down Expand Up @@ -1604,10 +1604,11 @@ private function generate_buildtime_environment_variables()
}

foreach ($sorted_environment_variables as $env) {
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
// For literal/multiline vars, real_value includes quotes that we need to remove
if ($env->is_literal || $env->is_multiline) {
// Strip outer quotes from real_value and apply proper bash escaping
$value = trim($env->real_value, "'");
$value = trim($resolvedValue, "'");
$escapedValue = escapeBashEnvValue($value);

if (isDev() && isset($envs_dict[$env->key])) {
Expand All @@ -1619,13 +1620,13 @@ private function generate_buildtime_environment_variables()
if (isDev()) {
$this->application_deployment_queue->addLogEntry("[DEBUG] Build-time env: {$env->key}");
$this->application_deployment_queue->addLogEntry('[DEBUG] Type: literal/multiline');
$this->application_deployment_queue->addLogEntry("[DEBUG] raw real_value: {$env->real_value}");
$this->application_deployment_queue->addLogEntry("[DEBUG] raw real_value: {$resolvedValue}");
$this->application_deployment_queue->addLogEntry("[DEBUG] stripped value: {$value}");
$this->application_deployment_queue->addLogEntry("[DEBUG] final escaped: {$escapedValue}");
}
} else {
// For normal vars, use double quotes to allow $VAR expansion
$escapedValue = escapeBashDoubleQuoted($env->real_value);
$escapedValue = escapeBashDoubleQuoted($resolvedValue);

if (isDev() && isset($envs_dict[$env->key])) {
$this->application_deployment_queue->addLogEntry("[DEBUG] User override: {$env->key} (was: {$envs_dict[$env->key]}, now: {$escapedValue})");
Expand All @@ -1636,7 +1637,7 @@ private function generate_buildtime_environment_variables()
if (isDev()) {
$this->application_deployment_queue->addLogEntry("[DEBUG] Build-time env: {$env->key}");
$this->application_deployment_queue->addLogEntry('[DEBUG] Type: normal (allows expansion)');
$this->application_deployment_queue->addLogEntry("[DEBUG] real_value: {$env->real_value}");
$this->application_deployment_queue->addLogEntry("[DEBUG] real_value: {$resolvedValue}");
$this->application_deployment_queue->addLogEntry("[DEBUG] final escaped: {$escapedValue}");
}
}
Expand All @@ -1655,10 +1656,11 @@ private function generate_buildtime_environment_variables()
}

foreach ($sorted_environment_variables as $env) {
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
// For literal/multiline vars, real_value includes quotes that we need to remove
if ($env->is_literal || $env->is_multiline) {
// Strip outer quotes from real_value and apply proper bash escaping
$value = trim($env->real_value, "'");
$value = trim($resolvedValue, "'");
$escapedValue = escapeBashEnvValue($value);

if (isDev() && isset($envs_dict[$env->key])) {
Expand All @@ -1670,13 +1672,13 @@ private function generate_buildtime_environment_variables()
if (isDev()) {
$this->application_deployment_queue->addLogEntry("[DEBUG] Build-time env: {$env->key}");
$this->application_deployment_queue->addLogEntry('[DEBUG] Type: literal/multiline');
$this->application_deployment_queue->addLogEntry("[DEBUG] raw real_value: {$env->real_value}");
$this->application_deployment_queue->addLogEntry("[DEBUG] raw real_value: {$resolvedValue}");
$this->application_deployment_queue->addLogEntry("[DEBUG] stripped value: {$value}");
$this->application_deployment_queue->addLogEntry("[DEBUG] final escaped: {$escapedValue}");
}
} else {
// For normal vars, use double quotes to allow $VAR expansion
$escapedValue = escapeBashDoubleQuoted($env->real_value);
$escapedValue = escapeBashDoubleQuoted($resolvedValue);

if (isDev() && isset($envs_dict[$env->key])) {
$this->application_deployment_queue->addLogEntry("[DEBUG] User override: {$env->key} (was: {$envs_dict[$env->key]}, now: {$escapedValue})");
Expand All @@ -1687,7 +1689,7 @@ private function generate_buildtime_environment_variables()
if (isDev()) {
$this->application_deployment_queue->addLogEntry("[DEBUG] Build-time env: {$env->key}");
$this->application_deployment_queue->addLogEntry('[DEBUG] Type: normal (allows expansion)');
$this->application_deployment_queue->addLogEntry("[DEBUG] real_value: {$env->real_value}");
$this->application_deployment_queue->addLogEntry("[DEBUG] real_value: {$resolvedValue}");
$this->application_deployment_queue->addLogEntry("[DEBUG] final escaped: {$escapedValue}");
}
}
Expand Down Expand Up @@ -2392,15 +2394,17 @@ private function generate_nixpacks_env_variables()
$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 !== '') {
$value = ($env->is_literal || $env->is_multiline) ? trim($env->real_value, "'") : $env->real_value;
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
if (! is_null($resolvedValue) && $resolvedValue !== '') {
$value = ($env->is_literal || $env->is_multiline) ? trim($resolvedValue, "'") : $resolvedValue;
$this->env_nixpacks_args->push('--env '.escapeShellValue("{$env->key}={$value}"));
}
}
} else {
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
if (! is_null($env->real_value) && $env->real_value !== '') {
$value = ($env->is_literal || $env->is_multiline) ? trim($env->real_value, "'") : $env->real_value;
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
if (! is_null($resolvedValue) && $resolvedValue !== '') {
$value = ($env->is_literal || $env->is_multiline) ? trim($resolvedValue, "'") : $resolvedValue;
$this->env_nixpacks_args->push('--env '.escapeShellValue("{$env->key}={$value}"));
}
}
Expand Down Expand Up @@ -2539,8 +2543,9 @@ private function generate_env_variables()
->get();

foreach ($envs as $env) {
if (! is_null($env->real_value)) {
$this->env_args->put($env->key, $env->real_value);
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
if (! is_null($resolvedValue)) {
$this->env_args->put($env->key, $resolvedValue);
}
}
} else {
Expand All @@ -2550,8 +2555,9 @@ private function generate_env_variables()
->get();

foreach ($envs as $env) {
if (! is_null($env->real_value)) {
$this->env_args->put($env->key, $env->real_value);
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
if (! is_null($resolvedValue)) {
$this->env_args->put($env->key, $resolvedValue);
}
}
}
Expand Down Expand Up @@ -3566,7 +3572,7 @@ private function generate_secrets_hash($variables)
} else {
$secrets_string = $variables
->map(function ($env) {
return "{$env->key}={$env->real_value}";
return "{$env->key}={$env->getResolvedValueWithServer($this->mainServer)}";
})
->sort()
->implode('|');
Expand Down Expand Up @@ -3632,7 +3638,7 @@ private function add_build_env_variables_to_dockerfile()
if (data_get($env, 'is_multiline') === true) {
$argsToInsert->push("ARG {$env->key}");
} else {
$argsToInsert->push("ARG {$env->key}={$env->real_value}");
$argsToInsert->push("ARG {$env->key}={$env->getResolvedValueWithServer($this->mainServer)}");
}
}
// Add Coolify variables as ARGs
Expand All @@ -3654,7 +3660,7 @@ private function add_build_env_variables_to_dockerfile()
if (data_get($env, 'is_multiline') === true) {
$argsToInsert->push("ARG {$env->key}");
} else {
$argsToInsert->push("ARG {$env->key}={$env->real_value}");
$argsToInsert->push("ARG {$env->key}={$env->getResolvedValueWithServer($this->mainServer)}");
}
}
// Add Coolify variables as ARGs
Expand Down Expand Up @@ -3690,7 +3696,7 @@ private function add_build_env_variables_to_dockerfile()
}
}
$envs_mapped = $envs->mapWithKeys(function ($env) {
return [$env->key => $env->real_value];
return [$env->key => $env->getResolvedValueWithServer($this->mainServer)];
});
$secrets_hash = $this->generate_secrets_hash($envs_mapped);
$argsToInsert->push("ARG COOLIFY_BUILD_SECRETS_HASH={$secrets_hash}");
Expand Down
61 changes: 61 additions & 0 deletions app/Livewire/Project/Shared/EnvironmentVariable/Add.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public function availableSharedVariables(): array
'team' => [],
'project' => [],
'environment' => [],
'server' => [],
];

// Early return if no team
Expand Down Expand Up @@ -126,6 +127,66 @@ public function availableSharedVariables(): array
}
}

// 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;
}

Expand Down
61 changes: 61 additions & 0 deletions app/Livewire/Project/Shared/EnvironmentVariable/Show.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ public function availableSharedVariables(): array
'team' => [],
'project' => [],
'environment' => [],
'server' => [],
];

// Early return if no team
Expand Down Expand Up @@ -274,6 +275,66 @@ public function availableSharedVariables(): array
}
}

// 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;
}

Expand Down
9 changes: 6 additions & 3 deletions app/Livewire/SharedVariables/Environment/Show.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@ public function saveKey($data)
}
}

public function mount()
public function mount(?string $project_uuid = null, ?string $environment_uuid = null)
{
$this->parameters = get_route_parameters();
$this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->firstOrFail();
$this->environment = $this->project->environments()->where('uuid', request()->route('environment_uuid'))->firstOrFail();
$projectUuid = $project_uuid ?? request()->route('project_uuid');
$environmentUuid = $environment_uuid ?? request()->route('environment_uuid');

$this->project = Project::ownedByCurrentTeam()->where('uuid', $projectUuid)->firstOrFail();
$this->environment = $this->project->environments()->where('uuid', $environmentUuid)->firstOrFail();
$this->getDevView();
}

Expand Down
4 changes: 2 additions & 2 deletions app/Livewire/SharedVariables/Project/Show.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ public function saveKey($data)
}
}

public function mount()
public function mount(?string $project_uuid = null)
{
$projectUuid = request()->route('project_uuid');
$projectUuid = $project_uuid ?? request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (! $project) {
Expand Down
22 changes: 22 additions & 0 deletions app/Livewire/SharedVariables/Server/Index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Livewire\SharedVariables\Server;

use App\Models\Server;
use Illuminate\Support\Collection;
use Livewire\Component;

class Index extends Component
{
public Collection $servers;

public function mount()
{
$this->servers = Server::ownedByCurrentTeamCached();
}

public function render()
{
return view('livewire.shared-variables.server.index');
}
}
Loading
Loading