mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
Compare commits
7 commits
30a79519b6
...
363ec2e0fd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
363ec2e0fd | ||
|
|
4015e03153 | ||
|
|
d0929a5883 | ||
|
|
86cbd82991 | ||
|
|
dd2fe5a3ac | ||
|
|
a5599da836 | ||
|
|
596f8bcf97 |
6 changed files with 115 additions and 55 deletions
|
|
@ -57,7 +57,9 @@ Thank you so much!
|
|||
|
||||
### Huge Sponsors
|
||||
|
||||
* [MVPS](https://www.mvps.net?ref=coolify.io) - Cheap VPS servers at the highest possible quality
|
||||
* [SerpAPI](https://serpapi.com?ref=coolify.io) - Google Search API — Scrape Google and other search engines from our fast, easy, and complete API
|
||||
*
|
||||
|
||||
### Big Sponsors
|
||||
|
||||
|
|
@ -85,7 +87,6 @@ Thank you so much!
|
|||
* [LiquidWeb](https://liquidweb.com?ref=coolify.io) - Premium managed hosting solutions
|
||||
* [Logto](https://logto.io?ref=coolify.io) - The better identity infrastructure for developers
|
||||
* [Macarne](https://macarne.com?ref=coolify.io) - Best IP Transit & Carrier Ethernet Solutions for Simplified Network Connectivity
|
||||
* [MVPS](https://www.mvps.net?ref=coolify.io) - Cheap VPS servers at the highest possible quality
|
||||
* [Mobb](https://vibe.mobb.ai/?ref=coolify.io) - Secure Your AI-Generated Code to Unlock Dev Productivity
|
||||
* [PFGLabs](https://pfglabs.com?ref=coolify.io) - Build Real Projects with Golang
|
||||
* [Ramnode](https://ramnode.com/?ref=coolify.io) - High Performance Cloud VPS Hosting
|
||||
|
|
@ -96,6 +97,7 @@ Thank you so much!
|
|||
* [Tigris](https://www.tigrisdata.com?ref=coolify.io) - Modern developer data platform
|
||||
* [Tolgee](https://tolgee.io?ref=coolify.io) - The open source localization platform
|
||||
* [Ubicloud](https://www.ubicloud.com?ref=coolify.io) - Open source cloud infrastructure platform
|
||||
* [VPSDime](https://vpsdime.com?ref=coolify.io) - Affordable high-performance VPS hosting solutions
|
||||
|
||||
|
||||
### Small Sponsors
|
||||
|
|
|
|||
|
|
@ -70,6 +70,9 @@ class Advanced extends Component
|
|||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $customInternalName = null;
|
||||
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $customContainerNamePrefix = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isGzipEnabled = true;
|
||||
|
||||
|
|
@ -111,6 +114,7 @@ class Advanced extends Component
|
|||
$this->application->settings->is_build_server_enabled = $this->isBuildServerEnabled;
|
||||
$this->application->settings->is_consistent_container_name_enabled = $this->isConsistentContainerNameEnabled;
|
||||
$this->application->settings->custom_internal_name = $this->customInternalName;
|
||||
$this->application->settings->custom_container_name_prefix = $this->customContainerNamePrefix;
|
||||
$this->application->settings->is_gzip_enabled = $this->isGzipEnabled;
|
||||
$this->application->settings->is_stripprefix_enabled = $this->isStripprefixEnabled;
|
||||
$this->application->settings->is_raw_compose_deployment_enabled = $this->isRawComposeDeploymentEnabled;
|
||||
|
|
@ -139,6 +143,7 @@ class Advanced extends Component
|
|||
$this->isBuildServerEnabled = $this->application->settings->is_build_server_enabled;
|
||||
$this->isConsistentContainerNameEnabled = $this->application->settings->is_consistent_container_name_enabled;
|
||||
$this->customInternalName = $this->application->settings->custom_internal_name;
|
||||
$this->customContainerNamePrefix = $this->application->settings->custom_container_name_prefix;
|
||||
$this->isRawComposeDeploymentEnabled = $this->application->settings->is_raw_compose_deployment_enabled;
|
||||
$this->isConnectToDockerNetworkEnabled = $this->application->settings->connect_to_docker_network;
|
||||
$this->disableBuildCache = $this->application->settings->disable_build_cache;
|
||||
|
|
@ -231,12 +236,19 @@ class Advanced extends Component
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->customContainerNamePrefix = null;
|
||||
$customInternalName = $this->customInternalName;
|
||||
$server = $this->application->destination->server;
|
||||
$allApplications = $server->applications();
|
||||
|
||||
$foundSameInternalName = $allApplications->filter(function ($application) {
|
||||
return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->customInternalName;
|
||||
if ($application->id === $this->application->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $application->settings->custom_internal_name === $this->customInternalName
|
||||
|| $application->settings->custom_container_name_prefix === $this->customInternalName;
|
||||
});
|
||||
if ($foundSameInternalName->isNotEmpty()) {
|
||||
$this->dispatch('error', 'This custom container name is already in use by another application on this server.');
|
||||
|
|
@ -252,6 +264,50 @@ class Advanced extends Component
|
|||
}
|
||||
}
|
||||
|
||||
public function saveCustomNamePrefix()
|
||||
{
|
||||
try {
|
||||
$this->authorize('update', $this->application);
|
||||
|
||||
if (str($this->customContainerNamePrefix)->isNotEmpty()) {
|
||||
$this->customContainerNamePrefix = str($this->customContainerNamePrefix)->slug()->value();
|
||||
} else {
|
||||
$this->customContainerNamePrefix = null;
|
||||
}
|
||||
|
||||
if (is_null($this->customContainerNamePrefix)) {
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Custom container name prefix saved.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->customInternalName = null;
|
||||
$server = $this->application->destination->server;
|
||||
$allApplications = $server->applications();
|
||||
|
||||
$foundDuplicate = $allApplications->filter(function ($application) {
|
||||
if ($application->id === $this->application->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $application->settings->custom_container_name_prefix === $this->customContainerNamePrefix
|
||||
|| $application->settings->custom_internal_name === $this->customContainerNamePrefix;
|
||||
});
|
||||
|
||||
if ($foundDuplicate->isNotEmpty()) {
|
||||
$this->dispatch('error', 'This custom container name prefix is already in use by another application on this server.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Custom container name prefix saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.application.advanced');
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ function generateApplicationContainerName(Application $application, $pull_reques
|
|||
// TODO: refactor generateApplicationContainerName, we do not need $application and $pull_request_id
|
||||
|
||||
$consistent_container_name = $application->settings->is_consistent_container_name_enabled;
|
||||
$custom_container_name_prefix = $application->settings->custom_container_name_prefix;
|
||||
$now = now()->format('Hisu');
|
||||
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||
return $application->uuid.'-pr-'.$pull_request_id;
|
||||
|
|
@ -189,7 +190,10 @@ function generateApplicationContainerName(Application $application, $pull_reques
|
|||
return $application->uuid;
|
||||
}
|
||||
|
||||
return $application->uuid.'-'.$now;
|
||||
|
||||
$prefix = $custom_container_name_prefix ?? $application->uuid;
|
||||
|
||||
return $prefix.'-'.$now;
|
||||
}
|
||||
}
|
||||
function get_port_from_dockerfile($dockerfile): ?int
|
||||
|
|
|
|||
|
|
@ -127,44 +127,10 @@ function connectProxyToNetworks(Server $server)
|
|||
return $commands->flatten();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate shell commands to fix a Docker network that has an IPv6 gateway with CIDR notation.
|
||||
*
|
||||
* Docker 25+ may store IPv6 gateways with CIDR (e.g. fd7d:f7d2:7e77::1/64), which causes
|
||||
* ParseAddr errors in Docker Compose. This detects the issue and recreates the network.
|
||||
*
|
||||
* @see https://github.com/coollabsio/coolify/issues/8649
|
||||
*
|
||||
* @param string $network Network name to check and fix
|
||||
* @return array Shell commands to execute on the remote server
|
||||
*/
|
||||
function fixNetworkIpv6CidrGateway(string $network): array
|
||||
{
|
||||
return [
|
||||
"if docker network inspect {$network} >/dev/null 2>&1; then",
|
||||
" IPV6_GW=\$(docker network inspect {$network} --format '{{range .IPAM.Config}}{{.Gateway}} {{end}}' 2>/dev/null | tr ' ' '\n' | grep '/' || true)",
|
||||
' if [ -n "$IPV6_GW" ]; then',
|
||||
" echo \"Fixing network {$network}: IPv6 gateway has CIDR notation (\$IPV6_GW)\"",
|
||||
" CONTAINERS=\$(docker network inspect {$network} --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null)",
|
||||
' for c in $CONTAINERS; do',
|
||||
" [ -n \"\$c\" ] && docker network disconnect {$network} \"\$c\" 2>/dev/null || true",
|
||||
' done',
|
||||
" docker network rm {$network} 2>/dev/null || true",
|
||||
" docker network create --attachable {$network} 2>/dev/null || true",
|
||||
' for c in $CONTAINERS; do',
|
||||
" [ -n \"\$c\" ] && [ \"\$c\" != \"coolify-proxy\" ] && docker network connect {$network} \"\$c\" 2>/dev/null || true",
|
||||
' done',
|
||||
' fi',
|
||||
'fi',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures all required networks exist before docker compose up.
|
||||
* This must be called BEFORE docker compose up since the compose file declares networks as external.
|
||||
*
|
||||
* Also detects and fixes networks with IPv6 CIDR gateway notation that causes ParseAddr errors.
|
||||
*
|
||||
* @param Server $server The server to ensure networks on
|
||||
* @return \Illuminate\Support\Collection Commands to create networks if they don't exist
|
||||
*/
|
||||
|
|
@ -174,23 +140,17 @@ function ensureProxyNetworksExist(Server $server)
|
|||
|
||||
if ($server->isSwarm()) {
|
||||
$commands = $networks->map(function ($network) {
|
||||
return array_merge(
|
||||
fixNetworkIpv6CidrGateway($network),
|
||||
[
|
||||
"echo 'Ensuring network $network exists...'",
|
||||
"docker network ls --format '{{.Name}}' | grep -q '^{$network}$' || docker network create --driver overlay --attachable $network",
|
||||
]
|
||||
);
|
||||
return [
|
||||
"echo 'Ensuring network $network exists...'",
|
||||
"docker network ls --format '{{.Name}}' | grep -q '^{$network}$' || docker network create --driver overlay --attachable $network",
|
||||
];
|
||||
});
|
||||
} else {
|
||||
$commands = $networks->map(function ($network) {
|
||||
return array_merge(
|
||||
fixNetworkIpv6CidrGateway($network),
|
||||
[
|
||||
"echo 'Ensuring network $network exists...'",
|
||||
"docker network ls --format '{{.Name}}' | grep -q '^{$network}$' || docker network create --attachable $network",
|
||||
]
|
||||
);
|
||||
return [
|
||||
"echo 'Ensuring network $network exists...'",
|
||||
"docker network ls --format '{{.Name}}' | grep -q '^{$network}$' || docker network create --attachable $network",
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->string('custom_container_name_prefix')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('custom_container_name_prefix');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -63,10 +63,20 @@
|
|||
@if ($isConsistentContainerNameEnabled === false)
|
||||
<form class="flex items-end gap-2 " wire:submit.prevent='saveCustomName'>
|
||||
<x-forms.input
|
||||
helper="You can add a custom name for your container.<br><br>The name will be converted to slug format when you save it. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
|
||||
instantSave id="customInternalName" label="Custom Container Name" canGate="update"
|
||||
:canResource="$application" />
|
||||
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||
helper="You can add a custom name for your container.<br><br>The name will be converted to slug format when you save it. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span><br><br><span class='dark:text-red-500 text-red-600'>Cannot be used together with Custom Container Name Prefix.</span>"
|
||||
id="customInternalName" label="Custom Container Name" canGate="update"
|
||||
:canResource="$application" :disabled="!empty($customContainerNamePrefix)" />
|
||||
<x-forms.button canGate="update" :canResource="$application" type="submit"
|
||||
:disabled="!empty($customContainerNamePrefix)">Save</x-forms.button>
|
||||
</form>
|
||||
<form class="flex items-end gap-2 " wire:submit.prevent='saveCustomNamePrefix'>
|
||||
<x-forms.input
|
||||
helper="Set a custom prefix for your container name. The final name will be: <span class='font-bold'>your-prefix-timestamp</span>.<br><br>The name will be converted to slug format when you save it.<br><br>This option <span class='font-bold dark:text-success'>keeps rolling updates working!</span><br><br><span class='dark:text-red-500 text-red-600'>Cannot be used together with Custom Container Name.</span>"
|
||||
id="customContainerNamePrefix" label="Custom Container Name Prefix" canGate="update"
|
||||
:canResource="$application" placeholder="e.g. my-nextjs-app"
|
||||
:disabled="!empty($customInternalName)" />
|
||||
<x-forms.button canGate="update" :canResource="$application" type="submit"
|
||||
:disabled="!empty($customInternalName)">Save</x-forms.button>
|
||||
</form>
|
||||
@endif
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
|
|
|
|||
Loading…
Reference in a new issue