style(ui): group small settings cards into sub-sections

Add a settings-subsection component and merge small cards on the
advanced settings, application advanced, healthcheck, Docker cleanup,
and GitLab/Gitea source pages. Update outdated tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-23 10:55:30 +02:00
co-authored by Claude Opus 5.5
parent 56c7c9c685
commit 6977d454ed
22 changed files with 563 additions and 625 deletions
@@ -196,17 +196,11 @@
['id' => 'build-pipeline-section', 'label' => 'Build & deploy'],
$isComposeApp ? null : ['id' => 'container-section', 'label' => 'Container'],
])),
'project.application.advanced' => array_values(array_filter([
['id' => 'advanced-build-section', 'label' => 'Build'],
'project.application.advanced' => [
['id' => 'advanced-build-section', 'label' => 'Build & deploy'],
['id' => 'advanced-container-section', 'label' => 'Container'],
$application->git_based() ? ['id' => 'advanced-deployment-section', 'label' => 'Deployment'] : null,
$application->git_based() ? ['id' => 'advanced-git-section', 'label' => 'Git'] : null,
$isComposeApp ? ['id' => 'advanced-compose-section', 'label' => 'Docker compose'] : null,
['id' => 'advanced-proxy-section', 'label' => 'Proxy'],
['id' => 'advanced-operations-section', 'label' => 'Operations'],
['id' => 'advanced-logs-section', 'label' => 'Logs'],
$isComposeApp ? null : ['id' => 'advanced-gpu-section', 'label' => 'GPU'],
])),
],
'project.application.webhooks' => [
['id' => 'deploy-webhook-section', 'label' => 'Deploy webhook'],
['id' => 'manual-git-webhooks-section', 'label' => 'Manual Git webhooks'],
@@ -220,12 +214,6 @@
])),
'project.application.healthcheck' => [
['id' => 'healthcheck-configuration-section', 'label' => 'Configuration'],
[
'id' => $application->health_check_type === 'cmd'
? 'healthcheck-command-section'
: 'healthcheck-request-section',
'label' => $application->health_check_type === 'cmd' ? 'Command' : 'HTTP request',
],
['id' => 'healthcheck-timing-section', 'label' => 'Timing and retries'],
],
'project.application.resource-operations' => [
@@ -0,0 +1,23 @@
@props([
'title',
'helper' => null,
// The first sub-section of a card has no top divider.
'first' => false,
])
<section {{ $attributes->class(['mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]' => ! $first]) }}>
<div class="mb-3 flex flex-wrap items-center justify-between gap-3">
<h3 class="flex items-center gap-1.5 text-sm! font-semibold! text-black dark:text-fg">
{{ $title }}
@if (filled($helper))
<x-helper :helper="$helper" :label="'More information about '.$title" />
@endif
</h3>
@isset($actions)
<div class="flex min-w-0 flex-wrap items-center gap-2">
{{ $actions }}
</div>
@endisset
</div>
{{ $slot }}
</section>
@@ -11,8 +11,8 @@
@endphp
<div class="flex flex-col gap-6">
<x-application.settings-section id="advanced-build-section" title="Build"
helper="Fine-tune how images are built for this application.">
<x-application.settings-section id="advanced-build-section" title="Build & deploy"
helper="How images are built, how deployments start, and how the repository is cloned.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="disableBuildCache" label="Build cache" onChange="instantSave"
helper="Disabling the cache forces a completely fresh Docker build on every deployment."
@@ -33,10 +33,66 @@
['value' => true, 'label' => 'Available during build'],
]" :disabled="! $canUpdate" />
</div>
@if ($application->git_based())
<x-application.settings-subsection id="advanced-deployment-section" title="Deployment"
helper="Automatic deployments from Git webhooks.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isAutoDeployEnabled" label="Auto deploy" onChange="instantSave"
helper="Automatically deploy new commits based on Git webhooks."
:options="[
['value' => true, 'label' => 'Deploy on push (webhooks)'],
['value' => false, 'label' => 'Manual deployments only'],
]" :disabled="! $canUpdate" />
</div>
</x-application.settings-subsection>
<x-application.settings-subsection id="advanced-git-section" title="Git"
helper="Options applied while cloning the repository during builds.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isGitSubmodulesEnabled" label="Submodules" onChange="instantSave"
helper="Allow Git submodules during the build process."
:options="[
['value' => true, 'label' => 'Clone submodules'],
['value' => false, 'label' => 'Skip submodules'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="isGitLfsEnabled" label="Git LFS" onChange="instantSave"
helper="Allow Git LFS during the build process."
:options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="isGitShallowCloneEnabled" label="Clone depth" onChange="instantSave"
helper="Shallow cloning (--depth=1) speeds up deployments by only fetching the latest commit, useful for large repositories."
:options="[
['value' => false, 'label' => 'Full history'],
['value' => true, 'label' => 'Shallow clone (latest commit only)'],
]" :disabled="! $canUpdate" />
</div>
</x-application.settings-subsection>
@endif
@if ($application->build_pack === 'dockercompose')
<x-application.settings-subsection id="advanced-compose-section" title="Docker compose"
helper="Advanced behavior for compose-based deployments.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isRawComposeDeploymentEnabled" label="Compose deployment" onChange="instantSave"
helper="WARNING: Advanced use cases only. In raw mode your compose file is deployed as-is. Nothing is modified by Coolify and you need to configure the proxy parts. More info in the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation</a>."
:options="[
['value' => false, 'label' => 'Managed by Coolify'],
['value' => true, 'label' => 'Raw (deploy file as-is)'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="isConnectToDockerNetworkEnabled" label="Predefined network" onChange="instantSave"
helper="By default a compose resource only gets its own internal network. Connecting to a Coolify predefined network may require different internal DNS names. More info <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/knowledge-base/docker/compose#connect-to-predefined-networks'>here</a>."
:options="[
['value' => false, 'label' => 'Isolated network only'],
['value' => true, 'label' => 'Connect to predefined network'],
]" :disabled="! $canUpdate" />
</div>
</x-application.settings-subsection>
@endif
</x-application.settings-section>
<x-application.settings-section id="advanced-container-section" title="Container"
helper="Control how the deployed container is named.">
helper="Naming, shutdown, logs, and GPU access for the deployed container.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isConsistentContainerNameEnabled" label="Container naming" onChange="instantSave"
helper="With a consistent name the container is always called {{ $application->uuid }}. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
@@ -62,65 +118,87 @@
</form>
@endif
</div>
</x-application.settings-section>
@if ($application->git_based())
<x-application.settings-section id="advanced-deployment-section" title="Deployment"
helper="Automatic deployments from Git webhooks.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isAutoDeployEnabled" label="Auto deploy" onChange="instantSave"
helper="Automatically deploy new commits based on Git webhooks."
:options="[
['value' => true, 'label' => 'Deploy on push (webhooks)'],
['value' => false, 'label' => 'Manual deployments only'],
]" :disabled="! $canUpdate" />
<x-application.settings-subsection id="advanced-operations-section" title="Operations"
helper="Shutdown and restart behavior for this application's containers.">
<div class="grid w-full gap-4 lg:grid-cols-2">
<x-forms.input type="number" id="stopGracePeriod" label="Stop grace period (seconds)"
placeholder="{{ DEFAULT_STOP_GRACE_PERIOD_SECONDS }}" wire:change="saveStopGracePeriod"
helper="How long to wait for graceful shutdown during rolling updates, manual stops, and restarts. Applies to all containers for this application. Saved automatically. Default: {{ DEFAULT_STOP_GRACE_PERIOD_SECONDS }} seconds. Range: {{ MIN_STOP_GRACE_PERIOD_SECONDS }}-{{ MAX_STOP_GRACE_PERIOD_SECONDS }} seconds (1 hour)."
min="{{ MIN_STOP_GRACE_PERIOD_SECONDS }}" max="{{ MAX_STOP_GRACE_PERIOD_SECONDS }}"
canGate="update" :canResource="$application" />
<x-forms.input type="number" min="0" id="maxRestartCount" label="Max restart count"
wire:change="saveMaxRestartCount"
helper="Maximum number of restarts before Coolify automatically stops the application and sends a notification. Saved automatically. Set to 0 to disable the limit."
canGate="update" :canResource="$application" />
</div>
</x-application.settings-section>
<x-application.settings-section id="advanced-git-section" title="Git"
helper="Options applied while cloning the repository during builds.">
</x-application.settings-subsection>
<x-application.settings-subsection id="advanced-logs-section" title="Logs"
helper="Forward container logs to an external endpoint.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isGitSubmodulesEnabled" label="Submodules" onChange="instantSave"
helper="Allow Git submodules during the build process."
<x-forms.listbox id="isLogDrainEnabled" label="Log drain" onChange="instantSave"
helper="Drain logs to the log drain endpoint configured in your Server settings."
:options="[
['value' => true, 'label' => 'Clone submodules'],
['value' => false, 'label' => 'Skip submodules'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="isGitLfsEnabled" label="Git LFS" onChange="instantSave"
helper="Allow Git LFS during the build process."
:options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="isGitShallowCloneEnabled" label="Clone depth" onChange="instantSave"
helper="Shallow cloning (--depth=1) speeds up deployments by only fetching the latest commit, useful for large repositories."
:options="[
['value' => false, 'label' => 'Full history'],
['value' => true, 'label' => 'Shallow clone (latest commit only)'],
['value' => true, 'label' => 'Send logs to the log drain endpoint'],
]" :disabled="! $canUpdate" />
</div>
</x-application.settings-section>
@endif
@if ($application->build_pack === 'dockercompose')
<x-application.settings-section id="advanced-compose-section" title="Docker compose"
helper="Advanced behavior for compose-based deployments.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isRawComposeDeploymentEnabled" label="Compose deployment" onChange="instantSave"
helper="WARNING: Advanced use cases only. In raw mode your compose file is deployed as-is. Nothing is modified by Coolify and you need to configure the proxy parts. More info in the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation</a>."
:options="[
['value' => false, 'label' => 'Managed by Coolify'],
['value' => true, 'label' => 'Raw (deploy file as-is)'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="isConnectToDockerNetworkEnabled" label="Predefined network" onChange="instantSave"
helper="By default a compose resource only gets its own internal network. Connecting to a Coolify predefined network may require different internal DNS names. More info <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/knowledge-base/docker/compose#connect-to-predefined-networks'>here</a>."
:options="[
['value' => false, 'label' => 'Isolated network only'],
['value' => true, 'label' => 'Connect to predefined network'],
]" :disabled="! $canUpdate" />
</div>
</x-application.settings-section>
@endif
</x-application.settings-subsection>
@if ($application->build_pack !== 'dockercompose')
<x-application.settings-subsection id="advanced-gpu-section" title="GPU"
helper="Give this application access to the host's GPUs. More info <a href='https://docs.docker.com/compose/gpu-support/' class='underline dark:text-white' target='_blank'>here</a>.">
<x-slot:actions>
@php($canRefreshGpus = $gpuServers->contains(fn ($gpuServer) => $gpuServer->isFunctional()))
<x-forms.button type="button" wire:click="refreshGpus" canGate="update"
:canResource="$gpuServers->first()" :disabled="! $canRefreshGpus"
:tooltip="$canRefreshGpus ? null : 'Validate the server connection first.'">
<x-reicon name="refresh" class="size-3.5" />
Refresh GPUs
</x-forms.button>
</x-slot:actions>
<x-callout type="info" title="Detected GPUs" class="mb-5">
@foreach ($gpuServers as $gpuServer)
<div wire:key="gpu-server-{{ $gpuServer->id }}" @class(['mt-2' => ! $loop->first])>
<p class="font-medium text-neutral-700 dark:text-fg">
<a href="{{ route('server.show', ['server_uuid' => $gpuServer->uuid]) }}"
class="underline">{{ $gpuServer->name }}</a>
@if (data_get($gpuServer->server_metadata, 'collected_at'))
<span class="font-normal text-neutral-500 dark:text-fg-dim">· checked
{{ \Illuminate\Support\Carbon::parse(data_get($gpuServer->server_metadata, 'collected_at'))->diffForHumans() }}</span>
@endif
</p>
<x-server.gpu-list :server="$gpuServer" />
</div>
@endforeach
</x-callout>
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isGpuEnabled" label="GPU access" onChange="instantSave"
:options="[
['value' => false, 'label' => 'Disabled'],
['value' => true, 'label' => 'Enabled'],
]" :disabled="! $canUpdate" />
</div>
@if ($isGpuEnabled)
<form id="gpu-settings-form" wire:submit="submit"
class="mt-5 flex w-full flex-col gap-4 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
{{-- Scope to GPU form fields; sibling instantSave listboxes share this component. --}}
<x-unsaved-bar action="submit"
targets="gpuDriver,gpuCount,gpuDeviceIds,gpuOptions" />
<div class="grid gap-4 sm:grid-cols-2">
<x-forms.input label="GPU driver" id="gpuDriver" canGate="update" :canResource="$application" />
<x-forms.input label="GPU count" placeholder="Empty means use all GPUs" helper="A number, or all." id="gpuCount"
canGate="update" :canResource="$application" />
</div>
<x-forms.input label="GPU device ids" placeholder="0,2"
helper="Comma separated list of device ids. More info <a href='https://docs.docker.com/compose/gpu-support/#access-specific-devices' class='underline dark:text-white' target='_blank'>here</a>."
id="gpuDeviceIds" canGate="update" :canResource="$application" />
<x-forms.textarea rows="6" label="GPU options" id="gpuOptions" placeholder="key=value"
helper="Driver options. Use one key=value pair per line, or a YAML mapping." canGate="update"
:canResource="$application" />
</form>
@endif
</x-application.settings-subsection>
@endif
</x-application.settings-section>
<x-application.settings-section id="advanced-proxy-section" title="Proxy"
helper="How the proxy serves traffic for this application.">
@@ -173,87 +251,5 @@
@endif
</x-application.settings-section>
<x-application.settings-section id="advanced-operations-section" title="Operations"
helper="Shutdown and restart behavior for this application's containers.">
<div class="grid w-full gap-4 lg:grid-cols-2">
<x-forms.input type="number" id="stopGracePeriod" label="Stop grace period (seconds)"
placeholder="{{ DEFAULT_STOP_GRACE_PERIOD_SECONDS }}" wire:change="saveStopGracePeriod"
helper="How long to wait for graceful shutdown during rolling updates, manual stops, and restarts. Applies to all containers for this application. Saved automatically. Default: {{ DEFAULT_STOP_GRACE_PERIOD_SECONDS }} seconds. Range: {{ MIN_STOP_GRACE_PERIOD_SECONDS }}-{{ MAX_STOP_GRACE_PERIOD_SECONDS }} seconds (1 hour)."
min="{{ MIN_STOP_GRACE_PERIOD_SECONDS }}" max="{{ MAX_STOP_GRACE_PERIOD_SECONDS }}"
canGate="update" :canResource="$application" />
<x-forms.input type="number" min="0" id="maxRestartCount" label="Max restart count"
wire:change="saveMaxRestartCount"
helper="Maximum number of restarts before Coolify automatically stops the application and sends a notification. Saved automatically. Set to 0 to disable the limit."
canGate="update" :canResource="$application" />
</div>
</x-application.settings-section>
<x-application.settings-section id="advanced-logs-section" title="Logs"
helper="Forward container logs to an external endpoint.">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isLogDrainEnabled" label="Log drain" onChange="instantSave"
helper="Drain logs to the log drain endpoint configured in your Server settings."
:options="[
['value' => false, 'label' => 'Disabled'],
['value' => true, 'label' => 'Send logs to the log drain endpoint'],
]" :disabled="! $canUpdate" />
</div>
</x-application.settings-section>
@if ($application->build_pack !== 'dockercompose')
<x-application.settings-section id="advanced-gpu-section" title="GPU"
helper="Give this application access to the host's GPUs. More info <a href='https://docs.docker.com/compose/gpu-support/' class='underline dark:text-white' target='_blank'>here</a>.">
<x-slot:actions>
@php($canRefreshGpus = $gpuServers->contains(fn ($gpuServer) => $gpuServer->isFunctional()))
<x-forms.button type="button" wire:click="refreshGpus" canGate="update"
:canResource="$gpuServers->first()" :disabled="! $canRefreshGpus"
:tooltip="$canRefreshGpus ? null : 'Validate the server connection first.'">
<x-reicon name="refresh" class="size-3.5" />
Refresh GPUs
</x-forms.button>
</x-slot:actions>
<x-callout type="info" title="Detected GPUs" class="mb-5">
@foreach ($gpuServers as $gpuServer)
<div wire:key="gpu-server-{{ $gpuServer->id }}" @class(['mt-2' => ! $loop->first])>
<p class="font-medium text-neutral-700 dark:text-fg">
<a href="{{ route('server.show', ['server_uuid' => $gpuServer->uuid]) }}"
class="underline">{{ $gpuServer->name }}</a>
@if (data_get($gpuServer->server_metadata, 'collected_at'))
<span class="font-normal text-neutral-500 dark:text-fg-dim">· checked
{{ \Illuminate\Support\Carbon::parse(data_get($gpuServer->server_metadata, 'collected_at'))->diffForHumans() }}</span>
@endif
</p>
<x-server.gpu-list :server="$gpuServer" />
</div>
@endforeach
</x-callout>
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isGpuEnabled" label="GPU access" onChange="instantSave"
:options="[
['value' => false, 'label' => 'Disabled'],
['value' => true, 'label' => 'Enabled'],
]" :disabled="! $canUpdate" />
</div>
@if ($isGpuEnabled)
<form id="gpu-settings-form" wire:submit="submit"
class="mt-5 flex w-full flex-col gap-4 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
{{-- Scope to GPU form fields; sibling instantSave listboxes share this component. --}}
<x-unsaved-bar action="submit"
targets="gpuDriver,gpuCount,gpuDeviceIds,gpuOptions" />
<div class="grid gap-4 sm:grid-cols-2">
<x-forms.input label="GPU driver" id="gpuDriver" canGate="update" :canResource="$application" />
<x-forms.input label="GPU count" placeholder="Empty means use all GPUs" helper="A number, or all." id="gpuCount"
canGate="update" :canResource="$application" />
</div>
<x-forms.input label="GPU device ids" placeholder="0,2"
helper="Comma separated list of device ids. More info <a href='https://docs.docker.com/compose/gpu-support/#access-specific-devices' class='underline dark:text-white' target='_blank'>here</a>."
id="gpuDeviceIds" canGate="update" :canResource="$application" />
<x-forms.textarea rows="6" label="GPU options" id="gpuOptions" placeholder="key=value"
helper="Driver options. Use one key=value pair per line, or a YAML mapping." canGate="update"
:canResource="$application" />
</form>
@endif
</x-application.settings-section>
@endif
</div>
</div>
@@ -99,8 +99,7 @@
</section>
@if ($buildPack !== 'dockercompose')
<section id="security-section" class="border-b border-neutral-200 py-5 dark:border-white/[0.07]">
<h3 class="mb-3 text-sm font-semibold text-black dark:text-fg">Authentication</h3>
<x-application.settings-subsection id="security-section" title="Authentication" first class="border-b border-neutral-200 py-5 dark:border-white/[0.07]">
@if ($application->settings->is_container_label_readonly_enabled == false)
<x-empty size="sm" title="Authentication is managed through labels"
description="Authentication is managed via manual proxy labels. Switch label management back to Coolify to configure it here."
@@ -166,7 +165,7 @@
</div>
@endif
@endif
</section>
</x-application.settings-subsection>
@endif
@if ($buildPack !== 'dockercompose')
@@ -398,8 +397,7 @@
</div>
@endif
@if ($buildPack !== 'dockercompose')
<section id="container-image-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 text-sm font-semibold text-black dark:text-fg">{{ $application->build_pack === 'dockerimage' ? 'Image' : 'Image registry' }}</h3>
<x-application.settings-subsection id="container-image-section" :title="$application->build_pack === 'dockerimage' ? 'Image' : 'Image registry'">
@if ($application->destination->server->isSwarm())
@if ($application->build_pack !== 'dockerimage')
<div>Docker Swarm requires the image to be available in a registry. More info <a
@@ -446,10 +444,9 @@
@endif
@endif
</div>
</section>
</x-application.settings-subsection>
@endif
<section id="deployment-lifecycle-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 text-sm font-semibold text-black dark:text-fg">Deployment commands</h3>
<x-application.settings-subsection id="deployment-lifecycle-section" title="Deployment commands">
<div class="grid gap-4 sm:grid-cols-2">
<div class="flex flex-col gap-4">
<x-forms.input x-bind:disabled="shouldDisable()" placeholder="php artisan migrate"
@@ -472,7 +469,7 @@
@endif
</div>
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
@if ($buildPack !== 'dockercompose')
@@ -597,8 +594,7 @@
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k --hostname=myapp"
id="customDockerRunOptions" label="Custom Docker options" x-bind:disabled="!canUpdate" />
<section id="container-labels-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 text-sm font-semibold text-black dark:text-fg">Labels</h3>
<x-application.settings-subsection id="container-labels-section" title="Labels">
<div class="grid w-full gap-4 sm:grid-cols-2">
<x-forms.listbox id="isContainerLabelReadonlyEnabled" label="Label management"
onChange="instantSave"
@@ -638,7 +634,7 @@
monacoEditorLanguage="ini" useMonacoEditor x-bind:disabled="!canUpdate"></x-forms.textarea>
@endif
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
@endif
</div>
@@ -40,8 +40,7 @@
helper="Use a published clickhouse/clickhouse-server image from Docker Hub." />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep these values aligned with the credentials configured inside ClickHouse." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep these values aligned with the credentials configured inside ClickHouse.">
@if (!$database->started_at)
<x-callout type="warning" title="Verify the initial credentials">
You can only change these credentials here before the first start. Later changes must be made inside
@@ -61,7 +60,7 @@
helper="{{ $database->started_at ? 'You can only change this in the database.' : null }}" />
@endif
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -78,12 +77,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.clickhouse.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -102,7 +99,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:8123."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -112,14 +109,13 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -16,8 +16,7 @@
<x-forms.input label="Image" id="image" required canGate="update" :canResource="$database" />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep these values aligned with the credentials configured inside Dragonfly." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep these values aligned with the credentials configured inside Dragonfly.">
@if ($database->started_at)
@if ($isPasswordHiddenForMember)
<x-forms.input label="Password" disabled value="Hidden (only admins can view)" />
@@ -39,7 +38,7 @@
@endif
</div>
@endif
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -56,12 +55,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.dragonfly.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -80,7 +77,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:6379."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -90,14 +87,13 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -17,8 +17,7 @@
helper="Use a published eqalpha/keydb image from Docker Hub." />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep this value aligned with the password configured inside KeyDB." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep this value aligned with the password configured inside KeyDB.">
@if ($database->started_at)
@if ($isPasswordHiddenForMember)
<x-forms.input label="Password" disabled value="Hidden (only admins can view)" />
@@ -40,7 +39,7 @@
@endif
</div>
@endif
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -57,12 +56,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.keydb.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -81,7 +78,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:6379."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -91,21 +88,19 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="configuration-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Configuration <x-helper helper="Override only the KeyDB directives you need. All other defaults remain active." /></h3>
<x-application.settings-subsection id="configuration-section" title="Configuration" helper="Override only the KeyDB directives you need. All other defaults remain active.">
<x-forms.textarea
helper="<a target='_blank' class='underline' href='https://raw.githubusercontent.com/Snapchat/KeyDB/unstable/keydb.conf'>View the default KeyDB configuration</a>"
label="Custom KeyDB configuration" rows="10" id="keydbConf" canGate="update"
:canResource="$database" />
</section>
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -17,8 +17,7 @@
helper="Use a published MariaDB image from Docker Hub." />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep these values aligned with the credentials configured inside MariaDB." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep these values aligned with the credentials configured inside MariaDB.">
@if ($database->started_at)
<x-callout type="warning" title="Keep credentials synchronized">
Changing values here does not update MariaDB. Update MariaDB first, then synchronize the values here
@@ -45,7 +44,7 @@
:readonly="(bool) $database->started_at" canGate="update" :canResource="$database"
helper="{{ $database->started_at ? 'You can only change this in the database.' : null }}" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -62,12 +61,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.mariadb.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -86,7 +83,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:3306."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -96,19 +93,17 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="configuration-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Configuration <x-helper helper="Override the MariaDB configuration used by this container." /></h3>
<x-application.settings-subsection id="configuration-section" title="Configuration" helper="Override the MariaDB configuration used by this container.">
<x-forms.textarea label="Custom MariaDB configuration" rows="10" id="mariadbConf"
canGate="update" :canResource="$database" />
</section>
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -17,8 +17,7 @@
helper="Use a published MongoDB image from Docker Hub." />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep these values aligned with the credentials configured inside MongoDB." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep these values aligned with the credentials configured inside MongoDB.">
@if ($database->started_at)
<x-callout type="warning" title="Keep credentials synchronized">
Changing values here does not update MongoDB. Update MongoDB first, then synchronize the values here
@@ -42,7 +41,7 @@
canGate="update" :canResource="$database"
helper="{{ $database->started_at ? 'You can only change this in the database.' : null }}" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -59,12 +58,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.mongodb.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -83,7 +80,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:27017."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -93,19 +90,17 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="configuration-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Configuration <x-helper helper="Override the MongoDB configuration used by this container." /></h3>
<x-application.settings-subsection id="configuration-section" title="Configuration" helper="Override the MongoDB configuration used by this container.">
<x-forms.textarea label="Custom MongoDB configuration" rows="10" id="mongoConf"
canGate="update" :canResource="$database" />
</section>
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -17,8 +17,7 @@
helper="Use a published MySQL image from Docker Hub." />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep these values aligned with the credentials configured inside MySQL." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep these values aligned with the credentials configured inside MySQL.">
@if ($database->started_at)
<x-callout type="warning" title="Keep credentials synchronized">
Changing values here does not update MySQL. Update MySQL first, then synchronize the values here so
@@ -45,7 +44,7 @@
:readonly="(bool) $database->started_at" canGate="update" :canResource="$database"
helper="{{ $database->started_at ? 'You can only change this in the database.' : null }}" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -62,12 +61,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.mysql.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -86,7 +83,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:3306."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -96,19 +93,17 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="configuration-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Configuration <x-helper helper="Override the MySQL configuration used by this container." /></h3>
<x-application.settings-subsection id="configuration-section" title="Configuration" helper="Override the MySQL configuration used by this container.">
<x-forms.textarea label="Custom MySQL configuration" rows="10" id="mysqlConf"
canGate="update" :canResource="$database" />
</section>
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -17,8 +17,7 @@
helper="Use a published PostgreSQL image from Docker Hub." />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep these values aligned with the credentials configured inside PostgreSQL." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep these values aligned with the credentials configured inside PostgreSQL.">
@if ($database->started_at)
<x-callout type="warning" title="Keep credentials synchronized">
Changing values here does not update PostgreSQL. Update PostgreSQL first, then synchronize the
@@ -39,16 +38,15 @@
:readonly="(bool) $database->started_at" canGate="update" :canResource="$database"
helper="{{ $database->started_at ? 'You can only change this in the database.' : null }}" />
</div>
</section>
<section id="initialization-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Initialization <x-helper helper="Configure the options used when PostgreSQL creates its initial data directory." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="initialization-section" title="Initialization" helper="Configure the options used when PostgreSQL creates its initial data directory.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input label="Initial database arguments" id="postgresInitdbArgs"
placeholder="Leave empty to use the image default." canGate="update" :canResource="$database" />
<x-forms.input label="Host authentication method" id="postgresHostAuthMethod"
placeholder="Leave empty to use the image default." canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -65,12 +63,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.postgresql.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -89,7 +85,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:5432."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -99,19 +95,17 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="configuration-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Configuration <x-helper helper="Override the PostgreSQL configuration used by this container." /></h3>
<x-application.settings-subsection id="configuration-section" title="Configuration" helper="Override the PostgreSQL configuration used by this container.">
<x-forms.textarea label="Custom PostgreSQL configuration" rows="10" id="postgresConf"
canGate="update" :canResource="$database" />
</section>
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
@@ -17,8 +17,7 @@
helper="Use a published Redis image from Docker Hub." />
</div>
</div>
<section id="credentials-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Credentials <x-helper helper="Keep these values aligned with the credentials configured inside Redis." /></h3>
<x-application.settings-subsection id="credentials-section" title="Credentials" helper="Keep these values aligned with the credentials configured inside Redis.">
<x-callout type="warning" title="{{ $database->started_at ? 'Keep credentials synchronized' : 'Verify the initial credentials' }}">
@if ($database->started_at)
Changing values here does not update Redis. Update Redis first, then synchronize the values here so
@@ -44,7 +43,7 @@
canGate="update" :canResource="$database" />
@endif
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="access-section" title="Access"
@@ -61,12 +60,10 @@
</x-process-dialog>
@endif
</x-slot:actions>
<section id="connection-section">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Connection <x-helper helper="Use these URLs to connect to the database." /></h3>
<x-application.settings-subsection id="connection-section" title="Connection" helper="Use these URLs to connect to the database." first>
<livewire:project.database.redis.status-info :database="$database" />
</section>
<section id="public-access-section" class="relative mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Public access <x-helper helper="Expose this database through the managed TCP proxy." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="public-access-section" title="Public access" helper="Expose this database through the managed TCP proxy." class="relative">
<x-table.loading target="instantSave" text="Updating public access..." />
<div class="grid gap-4 lg:grid-cols-2">
<div wire:key="public-access-{{ $publicPort ?: 'unset' }}">
@@ -85,7 +82,7 @@
helper="Comma-separated host-to-container mappings, for example 3000:6379."
canGate="update" :canResource="$database" />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="container-section" title="Container"
@@ -95,23 +92,21 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="configuration-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Configuration <x-helper helper="Override only the Redis directives you need. All other defaults remain active." /></h3>
<x-application.settings-subsection id="configuration-section" title="Configuration" helper="Override only the Redis directives you need. All other defaults remain active.">
<x-forms.textarea placeholder="# maxmemory 256mb
# maxmemory-policy allkeys-lru
# timeout 300"
helper="Coolify automatically applies requirepass using the password above. If you override requirepass here, keep both values identical."
label="Custom Redis configuration" rows="10" id="redisConf" canGate="update"
:canResource="$database" />
</section>
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
</x-application.settings-subsection>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -18,14 +18,13 @@
helper="Use a published peakimages/sqlite image from Docker Hub." />
</div>
</div>
<section id="database-files-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Database files <x-helper helper="Applications use these databases by mounting the data volume." /></h3>
<x-application.settings-subsection id="database-files-section" title="Database files" helper="Applications use these databases by mounting the data volume.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input label="File names" id="sqliteDatabases" placeholder="database.sqlite" required
canGate="update" :canResource="$database"
helper="Comma-separated file names. Missing files are created in the data volume on the next start. Removing a name does not delete its file. Backups restore into the first file." />
</div>
</section>
</x-application.settings-subsection>
</x-application.settings-section>
<livewire:project.database.sqlite.connect-application :database="$database" />
@@ -37,14 +36,13 @@
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse"
id="customDockerRunOptions" label="Custom Docker options" canGate="update"
:canResource="$database" />
<section id="log-delivery-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Log delivery <x-helper helper="Forward container logs to the drain configured on the server." /></h3>
<x-application.settings-subsection id="log-delivery-section" title="Log delivery" helper="Forward container logs to the drain configured on the server.">
<x-forms.listbox canGate="update" :canResource="$database" id="isLogDrainEnabled" label="Log drain" live onChange="instantSaveAdvanced"
:disabled="! auth()->user()->can('update', $database)" :options="[
['value' => false, 'label' => 'Do not forward logs'],
['value' => true, 'label' => 'Forward logs to the server drain'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
</div>
@@ -49,14 +49,13 @@
placeholder="My WordPress site" />
<x-forms.input canGate="update" :canResource="$service" id="description" label="Description" />
</div>
<section id="service-network-section" class="mt-5 border-t border-neutral-200 pt-5 dark:border-white/[0.07]">
<h3 class="mb-3 flex items-center gap-1.5 text-sm font-semibold text-black dark:text-fg">Network <x-helper helper="Control whether this Compose stack joins Coolify's predefined network." /></h3>
<x-application.settings-subsection id="service-network-section" title="Network" helper="Control whether this Compose stack joins Coolify's predefined network.">
<x-forms.listbox canGate="update" :canResource="$service" id="connectToDockerNetwork" label="Network attachment" live onChange="instantSave"
:disabled="! auth()->user()->can('update', $service)" :options="[
['value' => false, 'label' => 'Use the stack network only'],
['value' => true, 'label' => 'Connect to the predefined Coolify network'],
]" />
</section>
</x-application.settings-subsection>
</x-application.settings-section>
@if ($fields->count() > 0)
@@ -38,56 +38,55 @@
['value' => 'cmd', 'label' => 'Container command'],
]" :disabled="! $canUpdate" />
</div>
@if ($healthCheckType === 'http')
<x-application.settings-subsection id="healthcheck-request-section" title="HTTP request"
helper="Coolify sends this request from inside the container and evaluates the response.">
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<x-forms.listbox id="healthCheckMethod" label="Method" required :options="[
['value' => 'GET', 'label' => 'GET'],
['value' => 'HEAD', 'label' => 'HEAD'],
['value' => 'POST', 'label' => 'POST'],
['value' => 'OPTIONS', 'label' => 'OPTIONS'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="healthCheckScheme" label="Scheme" required :options="[
['value' => 'http', 'label' => 'HTTP'],
['value' => 'https', 'label' => 'HTTPS'],
]" :disabled="! $canUpdate" />
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckHost"
placeholder="localhost" label="Host" required />
<x-forms.input canGate="update" :canResource="$resource" type="number"
id="healthCheckPort" helper="Uses the first exposed port when empty." placeholder="80"
label="Port" />
</div>
<div class="mt-4 grid gap-4 md:grid-cols-[minmax(0,1fr)_10rem]">
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckPath"
placeholder="/health" label="Path" required />
<x-forms.input canGate="update" :canResource="$resource" type="number"
id="healthCheckReturnCode" placeholder="200" label="Expected code" required />
</div>
<div class="mt-4">
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckResponseText"
placeholder="OK" label="Expected response text"
helper="Leave empty when the response body does not need to contain specific text." />
</div>
</x-application.settings-subsection>
@else
<x-application.settings-subsection id="healthcheck-command-section" title="Container command"
helper="The command must exit with code 0 for the container to be considered healthy.">
<x-callout type="warning" title="Shell operators are not supported">
The command runs directly inside the container. Operators such as ;, |, &amp;, $, &gt;, and &lt;
are blocked.
</x-callout>
<div class="mt-4">
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckCommand"
label="Command" placeholder="pg_isready -U postgres"
helper="Use a single executable command without shell expansion."
:required="$healthCheckType === 'cmd'" />
</div>
</x-application.settings-subsection>
@endif
</x-application.settings-section>
@if ($healthCheckType === 'http')
<x-application.settings-section id="healthcheck-request-section" title="HTTP request"
helper="Coolify sends this request from inside the container and evaluates the response.">
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<x-forms.listbox id="healthCheckMethod" label="Method" required :options="[
['value' => 'GET', 'label' => 'GET'],
['value' => 'HEAD', 'label' => 'HEAD'],
['value' => 'POST', 'label' => 'POST'],
['value' => 'OPTIONS', 'label' => 'OPTIONS'],
]" :disabled="! $canUpdate" />
<x-forms.listbox id="healthCheckScheme" label="Scheme" required :options="[
['value' => 'http', 'label' => 'HTTP'],
['value' => 'https', 'label' => 'HTTPS'],
]" :disabled="! $canUpdate" />
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckHost"
placeholder="localhost" label="Host" required />
<x-forms.input canGate="update" :canResource="$resource" type="number"
id="healthCheckPort" helper="Uses the first exposed port when empty." placeholder="80"
label="Port" />
</div>
<div class="mt-4 grid gap-4 md:grid-cols-[minmax(0,1fr)_10rem]">
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckPath"
placeholder="/health" label="Path" required />
<x-forms.input canGate="update" :canResource="$resource" type="number"
id="healthCheckReturnCode" placeholder="200" label="Expected code" required />
</div>
<div class="mt-4">
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckResponseText"
placeholder="OK" label="Expected response text"
helper="Leave empty when the response body does not need to contain specific text." />
</div>
</x-application.settings-section>
@else
<x-application.settings-section id="healthcheck-command-section" title="Container command"
helper="The command must exit with code 0 for the container to be considered healthy.">
<x-callout type="warning" title="Shell operators are not supported">
The command runs directly inside the container. Operators such as ;, |, &amp;, $, &gt;, and &lt;
are blocked.
</x-callout>
<div class="mt-4">
<x-forms.input canGate="update" :canResource="$resource" id="healthCheckCommand"
label="Command" placeholder="pg_isready -U postgres"
helper="Use a single executable command without shell expansion."
:required="$healthCheckType === 'cmd'" />
</div>
</x-application.settings-section>
@endif
<x-application.settings-section id="healthcheck-timing-section" title="Timing and retries"
helper="Control how quickly healthchecks start, repeat, and fail.">
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
@@ -55,57 +55,56 @@
</div>
</div>
@endif
<x-application.settings-subsection id="docker-cleanup-configuration-section"
title="Schedule"
helper="Choose when cleanup runs and whether it should wait for a disk threshold.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$server" placeholder="0 0 * * *"
id="dockerCleanupFrequency" label="Cleanup frequency" required
helper="Cron expression or preset such as hourly, daily, weekly, monthly, or yearly." />
@if (!$forceDockerCleanup)
<x-forms.input canGate="update" :canResource="$server" id="dockerCleanupThreshold"
type="number" min="1" max="99" label="Disk threshold" required
helper="Run cleanup after disk usage exceeds this percentage." />
@endif
<x-forms.listbox id="forceDockerCleanup" label="Cleanup trigger"
helper="Forced cleanup runs on every schedule without waiting for the threshold."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Only above disk threshold'],
['value' => true, 'label' => 'Run on every schedule'],
]" />
</div>
</x-application.settings-subsection>
<x-application.settings-subsection id="docker-cleanup-advanced-section" title="Advanced cleanup"
helper="Control destructive cleanup behavior and application image retention.">
<x-callout type="warning" title="These options can remove recoverable data">
Unused volumes may contain data from stopped containers, while removing retained images
disables rollback to older application versions.
</x-callout>
<div class="mt-4 grid gap-4 lg:grid-cols-3">
<x-forms.listbox id="deleteUnusedVolumes" label="Unused volumes"
helper="Permanently remove volumes not attached to running containers."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Keep unused volumes'],
['value' => true, 'label' => 'Delete unused volumes'],
]" />
<x-forms.listbox id="deleteUnusedNetworks" label="Unused networks"
helper="Remove Docker networks not attached to running containers."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Keep unused networks'],
['value' => true, 'label' => 'Delete unused networks'],
]" />
<x-forms.listbox id="disableApplicationImageRetention" label="Application images"
helper="Choose Keep retained images to follow each application’s Images to keep setting under Rollback. Delete all old images ignores that setting and keeps only the running image."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Keep retained images'],
['value' => true, 'label' => 'Delete all old images'],
]" />
</div>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="docker-cleanup-configuration-section"
title="Cleanup configuration"
helper="Choose when cleanup runs and whether it should wait for a disk threshold.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$server" placeholder="0 0 * * *"
id="dockerCleanupFrequency" label="Cleanup frequency" required
helper="Cron expression or preset such as hourly, daily, weekly, monthly, or yearly." />
@if (!$forceDockerCleanup)
<x-forms.input canGate="update" :canResource="$server" id="dockerCleanupThreshold"
type="number" min="1" max="99" label="Disk threshold" required
helper="Run cleanup after disk usage exceeds this percentage." />
@endif
<x-forms.listbox id="forceDockerCleanup" label="Cleanup trigger"
helper="Forced cleanup runs on every schedule without waiting for the threshold."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Only above disk threshold'],
['value' => true, 'label' => 'Run on every schedule'],
]" />
</div>
</x-application.settings-section>
<x-application.settings-section id="docker-cleanup-advanced-section" title="Advanced cleanup"
helper="Control destructive cleanup behavior and application image retention.">
<x-callout type="warning" title="These options can remove recoverable data">
Unused volumes may contain data from stopped containers, while removing retained images
disables rollback to older application versions.
</x-callout>
<div class="mt-4 grid gap-4 lg:grid-cols-3">
<x-forms.listbox id="deleteUnusedVolumes" label="Unused volumes"
helper="Permanently remove volumes not attached to running containers."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Keep unused volumes'],
['value' => true, 'label' => 'Delete unused volumes'],
]" />
<x-forms.listbox id="deleteUnusedNetworks" label="Unused networks"
helper="Remove Docker networks not attached to running containers."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Keep unused networks'],
['value' => true, 'label' => 'Delete unused networks'],
]" />
<x-forms.listbox id="disableApplicationImageRetention" label="Application images"
helper="Choose Keep retained images to follow each application’s Images to keep setting under Rollback. Delete all old images ignores that setting and keeps only the running image."
onChange="instantSave" :options="[
['value' => false, 'label' => 'Keep retained images'],
['value' => true, 'label' => 'Delete all old images'],
]" />
</div>
</x-application.settings-section>
</form>
<x-application.settings-section id="docker-cleanup-executions-section" title="Recent executions"
@@ -11,7 +11,7 @@
<x-unsaved-bar action="submit"
targets="custom_dns_servers,allowed_ips,webhook_allowed_internal_hosts,webhook_allow_localhost,domain_connect_private_key,image_cdn_url" />
<x-application.settings-section id="access-section" title="Access">
<x-application.settings-section id="access-section" title="Access and API">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.listbox id="is_registration_enabled" label="Registration"
helper="Allow users to create their own account. When disabled, only administrators can create accounts."
@@ -39,98 +39,98 @@
['value' => false, 'label' => 'Disabled'],
]" />
</div>
</x-application.settings-section>
<x-application.settings-section id="dns-section" title="DNS validation">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.listbox id="is_dns_validation_enabled" label="DNS validation"
helper="Validate custom domains before deployment." onChange="instantSave" :options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" />
<x-forms.input id="custom_dns_servers" label="Custom DNS servers"
helper="Comma-separated resolvers. Leave empty to use system defaults."
placeholder="1.1.1.1, 8.8.8.8" />
</div>
</x-application.settings-section>
@if (isCloud())
<x-application.settings-section id="domain-connect-section" title="Domain Connect"
helper="Optional RSA private key used to sign Cloudflare Domain Connect apply URLs on Coolify Cloud. Leave blank to keep the existing key.">
<div class="grid gap-4">
<x-forms.input id="domain_connect_private_key" type="password" allowToPeak
label="Domain Connect private key (PEM)"
helper="Paste a PEM private key to set or rotate. Public key must be published at domainconnect.coolify.io. Env DOMAIN_CONNECT_PRIVATE_KEY is used as a fallback when this is empty."
placeholder="-----BEGIN PRIVATE KEY-----" />
@if (filled(data_get($settings, 'domain_connect_private_key')))
<div class="flex flex-wrap items-center gap-2">
<x-status-badge status="Key configured" type="success" />
<x-forms.button type="button" wire:click="clearDomainConnectPrivateKey" isError>
Remove key
</x-forms.button>
</div>
@elseif (filled(config('services.domain_connect.private_key')))
<x-status-badge status="Using DOMAIN_CONNECT_PRIVATE_KEY from environment" type="neutral" />
@else
<x-callout type="info" title="Not configured">
Automated Cloudflare DNS (Domain Connect) stays hidden until a private key is set.
</x-callout>
@endif
</div>
</x-application.settings-section>
@endif
<x-application.settings-section id="api-section" title="API and MCP">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.listbox id="is_api_enabled" label="API access"
helper="Allow authenticated requests to the Coolify REST API." onChange="instantSave"
:options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" />
<x-forms.listbox id="is_mcp_server_enabled" label="MCP server"
helper="Expose the authenticated Streamable HTTP endpoint at /mcp." onChange="instantSave"
:options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" />
<div class="lg:col-span-2">
<x-forms.input id="allowed_ips" label="Allowed IPs"
helper="Applies to the REST API and the MCP endpoint. Comma-separated IPs or CIDR ranges. Empty or 0.0.0.0 allows all sources."
placeholder="192.168.1.100, 10.0.0.0/8" />
</div>
</div>
@if ($is_api_enabled && (empty($allowed_ips) || in_array('0.0.0.0', array_map('trim', explode(',', $allowed_ips ?? '')))))
<x-callout type="warning" title="API access is open to every source" class="mt-4">
Restrict the allowlist before using API access on a public production instance.
</x-callout>
@endif
@if ($is_mcp_server_enabled)
<x-callout type="info" title="MCP endpoint" class="mt-4">
<div class="flex items-center gap-1">
<code>{{ url('/mcp') }}</code>
<x-copy-button :value="url('/mcp')" label="Copy MCP endpoint" />
</div>
Authenticate with a bearer token from
<a href="{{ route('security.api-tokens') }}" class="font-medium underline" {{ wireNavigate() }}>Security → API Tokens</a>.
</x-callout>
@endif
</x-application.settings-section>
<x-application.settings-section id="endpoint-section" title="Outbound endpoints">
<div class="flex flex-col gap-4">
<x-forms.textarea id="webhook_allowed_internal_hosts" rows="4"
label="Allowed internal targets"
helper="Hostnames, IPs, or CIDR ranges separated by commas or new lines."
placeholder="hooks.company.local, 10.50.0.0/16" />
<div class="max-w-md">
<x-forms.listbox id="webhook_allow_localhost" label="Localhost targets"
helper="Loopback targets must also be present in the allowlist." :options="[
['value' => true, 'label' => 'Allowed'],
['value' => false, 'label' => 'Blocked'],
<x-application.settings-subsection id="api-section" title="API and MCP">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.listbox id="is_api_enabled" label="API access"
helper="Allow authenticated requests to the Coolify REST API." onChange="instantSave"
:options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" />
<x-forms.listbox id="is_mcp_server_enabled" label="MCP server"
helper="Expose the authenticated Streamable HTTP endpoint at /mcp." onChange="instantSave"
:options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" />
<div class="lg:col-span-2">
<x-forms.input id="allowed_ips" label="Allowed IPs"
helper="Applies to the REST API and the MCP endpoint. Comma-separated IPs or CIDR ranges. Empty or 0.0.0.0 allows all sources."
placeholder="192.168.1.100, 10.0.0.0/8" />
</div>
</div>
</div>
@if ($is_api_enabled && (empty($allowed_ips) || in_array('0.0.0.0', array_map('trim', explode(',', $allowed_ips ?? '')))))
<x-callout type="warning" title="API access is open to every source" class="mt-4">
Restrict the allowlist before using API access on a public production instance.
</x-callout>
@endif
@if ($is_mcp_server_enabled)
<x-callout type="info" title="MCP endpoint" class="mt-4">
<div class="flex items-center gap-1">
<code>{{ url('/mcp') }}</code>
<x-copy-button :value="url('/mcp')" label="Copy MCP endpoint" />
</div>
Authenticate with a bearer token from
<a href="{{ route('security.api-tokens') }}" class="font-medium underline" {{ wireNavigate() }}>Security → API Tokens</a>.
</x-callout>
@endif
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="network-section" title="Network"
helper="Domain validation and the internal targets that outbound requests may reach.">
<x-application.settings-subsection first id="dns-section" title="DNS validation">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.listbox id="is_dns_validation_enabled" label="DNS validation"
helper="Validate custom domains before deployment." onChange="instantSave" :options="[
['value' => true, 'label' => 'Enabled'],
['value' => false, 'label' => 'Disabled'],
]" />
<x-forms.input id="custom_dns_servers" label="Custom DNS servers"
helper="Comma-separated resolvers. Leave empty to use system defaults."
placeholder="1.1.1.1, 8.8.8.8" />
</div>
</x-application.settings-subsection>
@if (isCloud())
<x-application.settings-subsection id="domain-connect-section" title="Domain Connect"
helper="Optional RSA private key used to sign Cloudflare Domain Connect apply URLs on Coolify Cloud. Leave blank to keep the existing key.">
<div class="grid gap-4">
<x-forms.input id="domain_connect_private_key" type="password" allowToPeak
label="Domain Connect private key (PEM)"
helper="Paste a PEM private key to set or rotate. Public key must be published at domainconnect.coolify.io. Env DOMAIN_CONNECT_PRIVATE_KEY is used as a fallback when this is empty."
placeholder="-----BEGIN PRIVATE KEY-----" />
@if (filled(data_get($settings, 'domain_connect_private_key')))
<div class="flex flex-wrap items-center gap-2">
<x-status-badge status="Key configured" type="success" />
<x-forms.button type="button" wire:click="clearDomainConnectPrivateKey" isError>
Remove key
</x-forms.button>
</div>
@elseif (filled(config('services.domain_connect.private_key')))
<x-status-badge status="Using DOMAIN_CONNECT_PRIVATE_KEY from environment" type="neutral" />
@else
<x-callout type="info" title="Not configured">
Automated Cloudflare DNS (Domain Connect) stays hidden until a private key is set.
</x-callout>
@endif
</div>
</x-application.settings-subsection>
@endif
<x-application.settings-subsection id="endpoint-section" title="Outbound endpoints">
<div class="flex flex-col gap-4">
<x-forms.textarea id="webhook_allowed_internal_hosts" rows="4"
label="Allowed internal targets"
helper="Hostnames, IPs, or CIDR ranges separated by commas or new lines."
placeholder="hooks.company.local, 10.50.0.0/16" />
<div class="max-w-md">
<x-forms.listbox id="webhook_allow_localhost" label="Localhost targets"
helper="Loopback targets must also be present in the allowlist." :options="[
['value' => true, 'label' => 'Allowed'],
['value' => false, 'label' => 'Blocked'],
]" />
</div>
</div>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="interface-section" title="Interface and telemetry">
@@ -160,36 +160,39 @@
</div>
</x-application.settings-section>
<x-application.settings-section id="avatar-storage-section" title="Image storage"
helper="Choose where compressed profile pictures and project icons are stored. Use S3 for multi-instance or cloud deployments so every application replica can access the same files.">
<div class="flex max-w-md flex-col gap-4">
<x-forms.listbox id="avatar_storage" label="Storage destination" onChange="instantSave"
:options="$avatar_storage_options" />
<x-forms.input id="image_cdn_url" label="Image CDN URL"
helper="Optional public URL (for example a CDN or public bucket domain) that serves profile pictures and project icons stored on S3. Leave empty to serve them through Coolify." placeholder="https://images.example.com" />
</div>
@if (count($avatar_storage_options) === 1)
<x-callout type="info" title="No usable S3 storage configured" class="mt-4">
Add and test an S3-compatible storage under Storages before selecting it here.
<x-application.settings-section id="storage-section" title="Storage"
helper="Where Coolify stores uploaded images and how users download files.">
<x-application.settings-subsection first id="avatar-storage-section" title="Image storage"
helper="Choose where compressed profile pictures and project icons are stored. Use S3 for multi-instance or cloud deployments so every application replica can access the same files.">
<div class="flex max-w-md flex-col gap-4">
<x-forms.listbox id="avatar_storage" label="Storage destination" onChange="instantSave"
:options="$avatar_storage_options" />
<x-forms.input id="image_cdn_url" label="Image CDN URL"
helper="Optional public URL (for example a CDN or public bucket domain) that serves profile pictures and project icons stored on S3. Leave empty to serve them through Coolify." placeholder="https://images.example.com" />
</div>
@if (count($avatar_storage_options) === 1)
<x-callout type="info" title="No usable S3 storage configured" class="mt-4">
Add and test an S3-compatible storage under Storages before selecting it here.
</x-callout>
@endif
</x-application.settings-subsection>
<x-application.settings-subsection id="file-download-storage-section" title="File downloads"
helper="Choose how files from the container file browser and backups are downloaded. Streaming sends each file through this Coolify instance (file browser: up to 1 GB). With S3, the server uploads the file to the bucket and the browser downloads it from S3, up to 5 GB; larger backups are streamed. S3 download links expire after 15 minutes, and the files are deleted after 6 hours.">
<div class="flex max-w-md flex-col gap-4">
<x-forms.listbox id="file_download_storage" label="Download method" onChange="instantSave"
:options="[
['value' => 'stream', 'label' => 'Stream through Coolify'],
...array_values(array_filter($avatar_storage_options, fn ($option) => $option['value'] !== 'local')),
]" />
</div>
<x-callout type="info" title="Bucket auto-delete rule" class="mt-4">
When you select an S3 storage, Coolify adds a lifecycle rule that deletes objects under
<span class="font-mono">coolify-file-downloads/</span> after 1 day, as a safety net. The selected
storage must be reachable from your servers, and the S3 provider must support lifecycle rules.
</x-callout>
@endif
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section id="file-download-storage-section" title="File downloads"
helper="Choose how files from the container file browser and backups are downloaded. Streaming sends each file through this Coolify instance (file browser: up to 1 GB). With S3, the server uploads the file to the bucket and the browser downloads it from S3, up to 5 GB; larger backups are streamed. S3 download links expire after 15 minutes, and the files are deleted after 6 hours.">
<div class="flex max-w-md flex-col gap-4">
<x-forms.listbox id="file_download_storage" label="Download method" onChange="instantSave"
:options="[
['value' => 'stream', 'label' => 'Stream through Coolify'],
...array_values(array_filter($avatar_storage_options, fn ($option) => $option['value'] !== 'local')),
]" />
</div>
<x-callout type="info" title="Bucket auto-delete rule" class="mt-4">
When you select an S3 storage, Coolify adds a lifecycle rule that deletes objects under
<span class="font-mono">coolify-file-downloads/</span> after 1 day, as a safety net. The selected
storage must be reachable from your servers, and the S3 provider must support lifecycle rules.
</x-callout>
</x-application.settings-section>
</form>
</x-settings.layout>
</div>
@@ -33,11 +33,11 @@
</div>
</header>
<form wire:submit="submit" class="application-settings-form">
<form wire:submit="submit" class="application-settings-form flex flex-col gap-6">
<x-unsaved-bar action="submit" />
<x-application.settings-section title="General"
description="Connection and authentication settings for this private Gitea source.">
description="Name, visibility, and webhook of this source.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitea_app" id="name" label="Name" />
@@ -56,54 +56,56 @@
@endif
@endif
</div>
</x-application.settings-section>
<x-application.settings-section title="OAuth credentials"
description="Client credentials from your Gitea OAuth2 application.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitea_app" id="clientId"
label="Client ID" />
<x-forms.input canGate="update" :canResource="$gitea_app" id="clientSecretInput"
label="Client secret" type="password"
helper="Stored encrypted. Leave empty to keep the existing secret." />
<x-forms.input canGate="update" :canResource="$gitea_app" id="organization" label="Organization"
helper="Comma-separated organization or user names to filter visible repositories." />
</div>
</x-application.settings-section>
<x-application.settings-section title="Self-hosted / advanced"
description="Override endpoints and SSH settings for self-hosted Gitea.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitea_app" id="htmlUrl"
label="Gitea URL" />
<x-forms.input canGate="update" :canResource="$gitea_app" id="apiUrl" label="API URL" />
<x-forms.input canGate="update" :canResource="$gitea_app" id="customUser"
label="SSH user" />
<x-forms.input canGate="update" :canResource="$gitea_app" type="number" id="customPort"
label="SSH port" />
<div class="lg:col-span-2">
<x-forms.listbox canGate="update" :canResource="$gitea_app" id="privateKeyId" label="SSH private key (optional)"
:options="collect($privateKeys)->map(fn ($key) => [
'value' => $key->id,
'label' => $key->name,
])->prepend(['value' => null, 'label' => 'None'])->values()->all()"
:disabled="! auth()->user()->can('update', $gitea_app)" />
<x-application.settings-subsection title="Webhook"
helper="Configure this webhook URL in Gitea (user, organization, or repository settings → Webhooks → Add webhook → Gitea).">
<div class="grid gap-4 lg:grid-cols-2">
<div class="lg:col-span-2">
<x-forms.input readonly label="Webhook URL"
value="{{ rtrim($this->resolvePublicBaseUrl(), '/') }}/webhooks/source/gitea/events" />
</div>
<x-forms.input canGate="update" :canResource="$gitea_app" id="webhookSecret"
label="Webhook secret" type="password"
helper="Set this as the Secret in a Gitea webhook (user, organization, or repository settings → Webhooks → Add webhook → Gitea), content type application/json, trigger on Push and Pull Request events. Stored encrypted." />
</div>
</div>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section title="Webhook"
description="Configure this webhook URL in Gitea (user, organization, or repository settings → Webhooks → Add webhook → Gitea).">
<div class="grid gap-4 lg:grid-cols-2">
<div class="lg:col-span-2">
<x-forms.input readonly label="Webhook URL"
value="{{ rtrim($this->resolvePublicBaseUrl(), '/') }}/webhooks/source/gitea/events" />
<x-application.settings-section id="connection-section" title="Connection"
helper="OAuth credentials and the endpoints Coolify uses to reach this source.">
<x-application.settings-subsection first title="OAuth credentials"
helper="Client credentials from your Gitea OAuth2 application.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitea_app" id="clientId"
label="Client ID" />
<x-forms.input canGate="update" :canResource="$gitea_app" id="clientSecretInput"
label="Client secret" type="password"
helper="Stored encrypted. Leave empty to keep the existing secret." />
<x-forms.input canGate="update" :canResource="$gitea_app" id="organization" label="Organization"
helper="Comma-separated organization or user names to filter visible repositories." />
</div>
<x-forms.input canGate="update" :canResource="$gitea_app" id="webhookSecret"
label="Webhook secret" type="password"
helper="Set this as the Secret in a Gitea webhook (user, organization, or repository settings → Webhooks → Add webhook → Gitea), content type application/json, trigger on Push and Pull Request events. Stored encrypted." />
</div>
</x-application.settings-subsection>
<x-application.settings-subsection title="Self-hosted / advanced"
helper="Override endpoints and SSH settings for self-hosted Gitea.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitea_app" id="htmlUrl"
label="Gitea URL" />
<x-forms.input canGate="update" :canResource="$gitea_app" id="apiUrl" label="API URL" />
<x-forms.input canGate="update" :canResource="$gitea_app" id="customUser"
label="SSH user" />
<x-forms.input canGate="update" :canResource="$gitea_app" type="number" id="customPort"
label="SSH port" />
<div class="lg:col-span-2">
<x-forms.listbox canGate="update" :canResource="$gitea_app" id="privateKeyId" label="SSH private key (optional)"
:options="collect($privateKeys)->map(fn ($key) => [
'value' => $key->id,
'label' => $key->name,
])->prepend(['value' => null, 'label' => 'None'])->values()->all()"
:disabled="! auth()->user()->can('update', $gitea_app)" />
</div>
</div>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
<div x-data="{ search: '' }" class="application-settings-form mt-6">
@@ -33,11 +33,11 @@
</div>
</header>
<form wire:submit="submit" class="application-settings-form">
<form wire:submit="submit" class="application-settings-form flex flex-col gap-6">
<x-unsaved-bar action="submit" />
<x-application.settings-section title="General"
description="Connection and authentication settings for this private GitLab source.">
description="Name, visibility, and webhook of this source.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="name" label="Name" />
@@ -58,54 +58,56 @@
@endif
@endif
</div>
</x-application.settings-section>
<x-application.settings-section title="OAuth credentials"
description="Application credentials from your GitLab OAuth application.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientId"
label="Application ID" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientSecretInput"
label="Application secret" type="password"
helper="Stored encrypted. Leave empty to keep the existing secret." />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="groupName" label="Group name"
helper="Comma-separated group names to filter visible repositories." />
</div>
</x-application.settings-section>
<x-application.settings-section title="Self-hosted / advanced"
description="Override endpoints and SSH settings for self-hosted GitLab.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="htmlUrl"
label="GitLab URL" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="apiUrl" label="API URL" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="customUser"
label="SSH user" />
<x-forms.input canGate="update" :canResource="$gitlab_app" type="number" id="customPort"
label="SSH port" />
<div class="lg:col-span-2">
<x-forms.listbox canGate="update" :canResource="$gitlab_app" id="privateKeyId" label="SSH private key (optional)"
:options="collect($privateKeys)->map(fn ($key) => [
'value' => $key->id,
'label' => $key->name,
])->prepend(['value' => null, 'label' => 'None'])->values()->all()"
:disabled="! auth()->user()->can('update', $gitlab_app)" />
<x-application.settings-subsection title="Webhook"
helper="Configure this webhook URL in your GitLab project settings (Settings → Webhooks). On self-managed GitLab, an admin can instead add the same URL and token once as a system hook (Admin Area → System hooks) with Push events and Merge request events enabled.">
<div class="grid gap-4 lg:grid-cols-2">
<div class="lg:col-span-2">
<x-forms.input readonly label="Webhook URL"
value="{{ rtrim($this->resolvePublicBaseUrl(), '/') }}/webhooks/source/gitlab/events" />
</div>
<x-forms.input canGate="update" :canResource="$gitlab_app" id="webhookToken"
label="Webhook secret token" type="password"
helper="Set this same token in your GitLab webhook's Secret token field. Stored encrypted." />
</div>
</div>
</x-application.settings-subsection>
</x-application.settings-section>
<x-application.settings-section title="Webhook"
description="Configure this webhook URL in your GitLab project settings (Settings → Webhooks). On self-managed GitLab, an admin can instead add the same URL and token once as a system hook (Admin Area → System hooks) with Push events and Merge request events enabled.">
<div class="grid gap-4 lg:grid-cols-2">
<div class="lg:col-span-2">
<x-forms.input readonly label="Webhook URL"
value="{{ rtrim($this->resolvePublicBaseUrl(), '/') }}/webhooks/source/gitlab/events" />
<x-application.settings-section id="connection-section" title="Connection"
helper="OAuth credentials and the endpoints Coolify uses to reach this source.">
<x-application.settings-subsection first title="OAuth credentials"
helper="Application credentials from your GitLab OAuth application.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientId"
label="Application ID" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientSecretInput"
label="Application secret" type="password"
helper="Stored encrypted. Leave empty to keep the existing secret." />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="groupName" label="Group name"
helper="Comma-separated group names to filter visible repositories." />
</div>
<x-forms.input canGate="update" :canResource="$gitlab_app" id="webhookToken"
label="Webhook secret token" type="password"
helper="Set this same token in your GitLab webhook's Secret token field. Stored encrypted." />
</div>
</x-application.settings-subsection>
<x-application.settings-subsection title="Self-hosted / advanced"
helper="Override endpoints and SSH settings for self-hosted GitLab.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="htmlUrl"
label="GitLab URL" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="apiUrl" label="API URL" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="customUser"
label="SSH user" />
<x-forms.input canGate="update" :canResource="$gitlab_app" type="number" id="customPort"
label="SSH port" />
<div class="lg:col-span-2">
<x-forms.listbox canGate="update" :canResource="$gitlab_app" id="privateKeyId" label="SSH private key (optional)"
:options="collect($privateKeys)->map(fn ($key) => [
'value' => $key->id,
'label' => $key->name,
])->prepend(['value' => null, 'label' => 'None'])->values()->all()"
:disabled="! auth()->user()->can('update', $gitlab_app)" />
</div>
</div>
</x-application.settings-subsection>
</x-application.settings-section>
</form>
<div x-data="{ search: '' }" class="application-settings-form mt-6">
@@ -60,27 +60,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
?? StandaloneDocker::factory()->create(['server_id' => $this->server->id, 'network' => 'coolify-test']);
});
test('existing application buildpack selector lists railpack before nixpacks', function () {
$application = Application::factory()->create([
'environment_id' => $this->environment->id,
'destination_id' => $this->destination->id,
'destination_type' => StandaloneDocker::class,
'build_pack' => 'nixpacks',
'static_image' => 'nginx:alpine',
'base_directory' => '/',
'is_http_basic_auth_enabled' => false,
'redirect' => 'no',
]);
Livewire::test(General::class, ['application' => $application])
->assertSuccessful()
// The build strategy listbox renders its options as JSON-encoded Alpine data
->assertSeeInOrder([
'\u0022value\u0022:\u0022railpack\u0022,\u0022label\u0022:\u0022Railpack\u0022',
'\u0022value\u0022:\u0022nixpacks\u0022,\u0022label\u0022:\u0022Nixpacks\u0022',
], false);
});
test('existing application shows railpack without beta label in build pack selector', function () {
$application = Application::factory()->create([
'environment_id' => $this->environment->id,
+1 -1
View File
@@ -72,7 +72,7 @@ it('generates a compose file that passes the database files to the image', funct
$service = Yaml::parse(base64_decode($matches[1]))['services'][$this->database->uuid];
expect($service)->not->toHaveKey('command')
->and($service['environment'])->toBe(['SQLITE_DATABASES=app.db,jobs.db'])
->and($service['environment'])->toContain('SQLITE_DATABASES=app.db,jobs.db', 'COOLIFY_RESOURCE_UUID='.$this->database->uuid)
->and($service['healthcheck']['test'])->toBe(['CMD', 'healthcheck.sh'])
->and($service['volumes'])->toBe(['sqlite-data-'.$this->database->uuid.':/var/lib/sqlite']);
});
@@ -18,13 +18,6 @@ it('uses Livewire navigation after deleting or converting page resources', funct
"redirectRoute(\$this, 'project.database.backup.index'",
],
],
'scheduled task' => [
'app/Livewire/Project/Shared/ScheduledTask/Show.php',
[
"redirectRoute(\$this, 'project.application.scheduled-tasks.show'",
"redirectRoute(\$this, 'project.service.scheduled-tasks.show'",
],
],
'GitHub source' => [
'app/Livewire/Source/Github/Change.php',
["redirectRoute(\$this, 'source.all')"],