diff --git a/.github/workflows/chore-manage-stale-issues-and-prs.yml b/.github/workflows/chore-manage-stale-issues-and-prs.yml
index 2afc996cb..58a2b7d7e 100644
--- a/.github/workflows/chore-manage-stale-issues-and-prs.yml
+++ b/.github/workflows/chore-manage-stale-issues-and-prs.yml
@@ -13,16 +13,16 @@ jobs:
id: stale
with:
stale-issue-message: 'This issue will be automatically closed in a few days if no response is received. Please provide an update with the requested information.'
- stale-pr-message: 'This pull request will be automatically closed in a few days if no response is received. Please update your PR or comment if you would like to continue working on it.'
+ stale-pr-message: 'This pull request requires attention. If no changes or response is received within the next few days, it will be automatically closed. Please update your PR or leave a comment with the requested information.'
close-issue-message: 'This issue has been automatically closed due to inactivity.'
- close-pr-message: 'This pull request has been automatically closed due to inactivity.'
+ close-pr-message: 'Thank you for your contribution. Due to inactivity, this PR was automatically closed. If you would like to continue working on this change in the future, feel free to reopen this PR or submit a new one.'
days-before-stale: 14
days-before-close: 7
stale-issue-label: '⏱︎ Stale'
stale-pr-label: '⏱︎ Stale'
- only-labels: '💤 Waiting for feedback'
+ only-labels: '💤 Waiting for feedback, 💤 Waiting for changes'
remove-stale-when-updated: true
operations-per-run: 100
- labels-to-remove-when-unstale: '⏱︎ Stale, 💤 Waiting for feedback'
+ labels-to-remove-when-unstale: '⏱︎ Stale, 💤 Waiting for feedback, 💤 Waiting for changes'
close-issue-reason: 'not_planned'
exempt-all-milestones: false
diff --git a/.github/workflows/chore-remove-labels-and-assignees-on-close.yml b/.github/workflows/chore-remove-labels-and-assignees-on-close.yml
index ea097e328..a3c299b5e 100644
--- a/.github/workflows/chore-remove-labels-and-assignees-on-close.yml
+++ b/.github/workflows/chore-remove-labels-and-assignees-on-close.yml
@@ -19,8 +19,12 @@ jobs:
script: |
const { owner, repo } = context.repo;
- async function processIssue(issueNumber) {
+ async function processIssue(issueNumber, isFromPR = false, prBaseBranch = null) {
try {
+ if (isFromPR && prBaseBranch !== 'main') {
+ return;
+ }
+
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
owner,
repo,
@@ -59,19 +63,19 @@ jobs:
}
}
- if (context.eventName === 'issues' || context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
- const issue = context.payload.issue || context.payload.pull_request;
- await processIssue(issue.number);
+ if (context.eventName === 'issues') {
+ await processIssue(context.payload.issue.number);
}
if (context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
const pr = context.payload.pull_request;
- if (pr.body) {
+ await processIssue(pr.number);
+ if (pr.merged && pr.base.ref === 'main' && pr.body) {
const issueReferences = pr.body.match(/#(\d+)/g);
if (issueReferences) {
for (const reference of issueReferences) {
const issueNumber = parseInt(reference.substring(1));
- await processIssue(issueNumber);
+ await processIssue(issueNumber, true, pr.base.ref);
}
}
}
diff --git a/.github/workflows/coolify-helper-next.yml b/.github/workflows/coolify-helper-next.yml
index 4354294b1..a4a2a21f6 100644
--- a/.github/workflows/coolify-helper-next.yml
+++ b/.github/workflows/coolify-helper-next.yml
@@ -38,7 +38,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getHelperVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -77,7 +77,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getHelperVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -119,7 +119,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getHelperVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
run: |
diff --git a/.github/workflows/coolify-helper.yml b/.github/workflows/coolify-helper.yml
index 6d852a2b3..78c888a01 100644
--- a/.github/workflows/coolify-helper.yml
+++ b/.github/workflows/coolify-helper.yml
@@ -38,7 +38,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getHelperVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -77,7 +77,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getHelperVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -119,7 +119,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getHelperVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
run: |
diff --git a/.github/workflows/coolify-realtime-next.yml b/.github/workflows/coolify-realtime-next.yml
index ef247170f..ad590146b 100644
--- a/.github/workflows/coolify-realtime-next.yml
+++ b/.github/workflows/coolify-realtime-next.yml
@@ -42,7 +42,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -82,7 +82,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -125,7 +125,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
run: |
diff --git a/.github/workflows/coolify-realtime.yml b/.github/workflows/coolify-realtime.yml
index 9654a21b0..d3af14144 100644
--- a/.github/workflows/coolify-realtime.yml
+++ b/.github/workflows/coolify-realtime.yml
@@ -42,7 +42,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -82,7 +82,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Build and Push Image
uses: docker/build-push-action@v6
@@ -125,7 +125,7 @@ jobs:
- name: Get Version
id: version
run: |
- echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT
- name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
run: |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 80ec0614e..dba3676cf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,6 +4,8 @@ # Contributing to Coolify
You can ask for guidance anytime on our [Discord server](https://coollabs.io/discord) in the `#contribute` channel.
+To understand the tech stack, please refer to the [Tech Stack](TECH_STACK.md) document.
+
## Table of Contents
1. [Setup Development Environment](#1-setup-development-environment)
diff --git a/TECH_STACK.md b/TECH_STACK.md
new file mode 100644
index 000000000..6a779eb29
--- /dev/null
+++ b/TECH_STACK.md
@@ -0,0 +1,29 @@
+# Coolify Technology Stack
+
+## Frontend
+
+- Livewire and Alpine.js
+- Blade (PHP templating engine)
+- Tailwind CSS
+- Monaco Editor (Code editor component)
+- XTerm.js (Terminal component)
+
+## Backend
+
+- Laravel 11 (PHP Framework)
+- PostgreSQL 15 (Database)
+- Redis 7 (Caching & Real-time features)
+- Soketi (WebSocket Server)
+
+## DevOps & Infrastructure
+
+- Docker & Docker Compose
+- Nginx (Web Server)
+- S6 Overlay (Process Supervisor)
+- GitHub Actions (CI/CD)
+
+## Languages
+
+- PHP 8.4
+- JavaScript
+- Shell/Bash scripts
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 37c73d10f..a6d3bc1a2 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -1122,7 +1122,8 @@ private function laravel_finetunes()
$nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH';
$nixpacks_php_fallback_path->value = '/index.php';
$nixpacks_php_fallback_path->is_build_time = false;
- $nixpacks_php_fallback_path->application_id = $this->application->id;
+ $nixpacks_php_fallback_path->resourceable_id = $this->application->id;
+ $nixpacks_php_fallback_path->resourceable_type = 'App\Models\Application';
$nixpacks_php_fallback_path->save();
}
if (! $nixpacks_php_root_dir) {
@@ -1130,7 +1131,8 @@ private function laravel_finetunes()
$nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR';
$nixpacks_php_root_dir->value = '/app/public';
$nixpacks_php_root_dir->is_build_time = false;
- $nixpacks_php_root_dir->application_id = $this->application->id;
+ $nixpacks_php_root_dir->resourceable_id = $this->application->id;
+ $nixpacks_php_root_dir->resourceable_type = 'App\Models\Application';
$nixpacks_php_root_dir->save();
}
@@ -2286,8 +2288,16 @@ private function start_by_compose_file()
private function generate_build_env_variables()
{
- $variables = collect($this->nixpacks_plan_json->get('variables'));
+ if ($this->application->build_pack === 'nixpacks') {
+ $variables = collect($this->nixpacks_plan_json->get('variables'));
+ } else {
+ $this->generate_env_variables();
+ $variables = collect([])->merge($this->env_args);
+ }
+
$this->build_args = $variables->map(function ($value, $key) {
+ $value = escapeshellarg($value);
+
return "--build-arg {$key}={$value}";
});
}
diff --git a/app/Jobs/PullTemplatesFromCDN.php b/app/Jobs/PullTemplatesFromCDN.php
index 45c536e06..9a4c991bc 100644
--- a/app/Jobs/PullTemplatesFromCDN.php
+++ b/app/Jobs/PullTemplatesFromCDN.php
@@ -25,7 +25,7 @@ public function __construct()
public function handle(): void
{
try {
- if (isDev() || isCloud()) {
+ if (isDev()) {
return;
}
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 576f87801..f8e28d216 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -442,6 +442,7 @@ public function downloadConfig()
{
$config = GenerateConfig::run($this->application, true);
$fileName = str($this->application->name)->slug()->append('_config.json');
+ dd($config);
return response()->streamDownload(function () use ($config) {
echo $config;
diff --git a/app/Livewire/Project/Database/Redis/General.php b/app/Livewire/Project/Database/Redis/General.php
index 25a96b292..05babeaec 100644
--- a/app/Livewire/Project/Database/Redis/General.php
+++ b/app/Livewire/Project/Database/Redis/General.php
@@ -88,12 +88,12 @@ public function submit()
if (version_compare($this->redis_version, '6.0', '>=')) {
$this->database->runtime_environment_variables()->updateOrCreate(
['key' => 'REDIS_USERNAME'],
- ['value' => $this->redis_username, 'standalone_redis_id' => $this->database->id]
+ ['value' => $this->redis_username, 'resourceable_id' => $this->database->id]
);
}
$this->database->runtime_environment_variables()->updateOrCreate(
['key' => 'REDIS_PASSWORD'],
- ['value' => $this->redis_password, 'standalone_redis_id' => $this->database->id]
+ ['value' => $this->redis_password, 'resourceable_id' => $this->database->id]
);
$this->database->save();
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
index 4b66bfdcb..3a7d0faa5 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
@@ -77,18 +77,28 @@ public function refresh()
public function syncData(bool $toModel = false)
{
if ($toModel) {
- $this->validate();
+ if ($this->isSharedVariable) {
+ $this->validate([
+ 'key' => 'required|string',
+ 'value' => 'nullable',
+ 'is_multiline' => 'required|boolean',
+ 'is_literal' => 'required|boolean',
+ 'is_shown_once' => 'required|boolean',
+ 'real_value' => 'nullable',
+ ]);
+ } else {
+ $this->validate();
+ $this->env->is_build_time = $this->is_build_time;
+ $this->env->is_required = $this->is_required;
+ $this->env->is_shared = $this->is_shared;
+ }
$this->env->key = $this->key;
$this->env->value = $this->value;
- $this->env->is_build_time = $this->is_build_time;
$this->env->is_multiline = $this->is_multiline;
$this->env->is_literal = $this->is_literal;
$this->env->is_shown_once = $this->is_shown_once;
- $this->env->is_required = $this->is_required;
- $this->env->is_shared = $this->is_shared;
$this->env->save();
} else {
-
$this->key = $this->env->key;
$this->value = $this->env->value;
$this->is_build_time = $this->env->is_build_time ?? false;
@@ -141,30 +151,15 @@ public function instantSave()
public function submit()
{
try {
- if ($this->isSharedVariable) {
- $this->validate([
- 'key' => 'required|string',
- 'value' => 'nullable',
- 'is_shown_once' => 'required|boolean',
- ]);
- } else {
- $this->validate();
- }
-
if (! $this->isSharedVariable && $this->is_required && str($this->value)->isEmpty()) {
$oldValue = $this->env->getOriginal('value');
$this->value = $oldValue;
- $this->dispatch('error', 'Required environment variable cannot be empty.');
+ $this->dispatch('error', 'Required environment variables cannot be empty.');
return;
}
$this->serialize();
-
- if ($this->isSharedVariable) {
- unset($this->is_required);
- }
-
$this->syncData(true);
$this->dispatch('success', 'Environment variable updated.');
$this->dispatch('envsUpdated');
diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php
index d12d8e26a..f993480c7 100644
--- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php
+++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php
@@ -27,6 +27,8 @@ class ExecuteContainerCommand extends Component
public Collection $servers;
+ public bool $hasShell = true;
+
protected $rules = [
'server' => 'required',
'container' => 'required',
@@ -141,6 +143,16 @@ public function loadContainers()
}
}
+ private function checkShellAvailability(Server $server, string $container): bool
+ {
+ $escapedContainer = escapeshellarg($container);
+ $result = instant_remote_process([
+ "docker exec {$escapedContainer} which bash || docker exec {$escapedContainer} which sh",
+ ], $server, false);
+
+ return ! empty($result);
+ }
+
#[On('connectToServer')]
public function connectToServer()
{
@@ -148,6 +160,7 @@ public function connectToServer()
if ($this->server->isForceDisabled()) {
throw new \RuntimeException('Server is disabled.');
}
+ $this->hasShell = true;
$this->dispatch(
'send-terminal-command',
false,
@@ -201,6 +214,11 @@ public function connectToContainer()
throw new \RuntimeException('Server ownership verification failed.');
}
+ $this->hasShell = $this->checkShellAvailability($server, data_get($container, 'container.Names'));
+ if (! $this->hasShell) {
+ return;
+ }
+
$this->dispatch(
'send-terminal-command',
true,
diff --git a/app/Models/Application.php b/app/Models/Application.php
index 289ef5b0f..3913ce37a 100644
--- a/app/Models/Application.php
+++ b/app/Models/Application.php
@@ -3,6 +3,8 @@
namespace App\Models;
use App\Enums\ApplicationDeploymentStatus;
+use App\Services\ConfigurationGenerator;
+use App\Traits\HasConfiguration;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -105,7 +107,7 @@
class Application extends BaseModel
{
- use HasFactory, SoftDeletes;
+ use HasConfiguration, HasFactory, SoftDeletes;
private static $parserVersion = '4';
@@ -1640,35 +1642,28 @@ public function getMemoryMetrics(int $mins = 5)
}
}
+ public function getLimits(): array
+ {
+ return [
+ 'limits_memory' => $this->limits_memory,
+ 'limits_memory_swap' => $this->limits_memory_swap,
+ 'limits_memory_swappiness' => $this->limits_memory_swappiness,
+ 'limits_memory_reservation' => $this->limits_memory_reservation,
+ 'limits_cpus' => $this->limits_cpus,
+ 'limits_cpuset' => $this->limits_cpuset,
+ 'limits_cpu_shares' => $this->limits_cpu_shares,
+ ];
+ }
+
public function generateConfig($is_json = false)
{
- $config = collect([]);
- if ($this->build_pack = 'nixpacks') {
- $config = collect([
- 'build_pack' => 'nixpacks',
- 'docker_registry_image_name' => $this->docker_registry_image_name,
- 'docker_registry_image_tag' => $this->docker_registry_image_tag,
- 'install_command' => $this->install_command,
- 'build_command' => $this->build_command,
- 'start_command' => $this->start_command,
- 'base_directory' => $this->base_directory,
- 'publish_directory' => $this->publish_directory,
- 'custom_docker_run_options' => $this->custom_docker_run_options,
- 'ports_exposes' => $this->ports_exposes,
- 'ports_mappings' => $this->ports_mapping,
- 'settings' => collect([
- 'is_static' => $this->settings->is_static,
- ]),
- ]);
- }
- $config = $config->filter(function ($value) {
- return str($value)->isNotEmpty();
- });
+ $generator = new ConfigurationGenerator($this);
+
if ($is_json) {
- return json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+ return $generator->toJson();
}
- return $config;
+ return $generator->toArray();
}
public function setConfig($config)
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index 507ff0d7e..5f686de60 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -4,9 +4,7 @@
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
use Illuminate\Database\Eloquent\Casts\Attribute;
-use Illuminate\Database\Eloquent\Model;
use OpenApi\Attributes as OA;
-use Visus\Cuid2\Cuid2;
#[OA\Schema(
description: 'Environment Variable model',
@@ -30,7 +28,7 @@
'updated_at' => ['type' => 'string'],
]
)]
-class EnvironmentVariable extends Model
+class EnvironmentVariable extends BaseModel
{
protected $guarded = [];
@@ -49,12 +47,6 @@ class EnvironmentVariable extends Model
protected static function booted()
{
- static::creating(function (Model $model) {
- if (! $model->uuid) {
- $model->uuid = (string) new Cuid2;
- }
- });
-
static::created(function (EnvironmentVariable $environment_variable) {
if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) {
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)
diff --git a/app/Notifications/Channels/EmailChannel.php b/app/Notifications/Channels/EmailChannel.php
index 6ffe5c4d7..98536d346 100644
--- a/app/Notifications/Channels/EmailChannel.php
+++ b/app/Notifications/Channels/EmailChannel.php
@@ -53,6 +53,7 @@ private function bootConfigs($notifiable): void
if (! $type) {
throw new Exception('No email settings found.');
}
+ config()->set('mail.default', $type);
return;
}
diff --git a/app/Services/ConfigurationGenerator.php b/app/Services/ConfigurationGenerator.php
new file mode 100644
index 000000000..a7e4b31be
--- /dev/null
+++ b/app/Services/ConfigurationGenerator.php
@@ -0,0 +1,194 @@
+generateConfig();
+ }
+
+ protected function generateConfig(): void
+ {
+ if ($this->resource instanceof Application) {
+ $this->config = [
+ 'id' => $this->resource->id,
+ 'name' => $this->resource->name,
+ 'uuid' => $this->resource->uuid,
+ 'description' => $this->resource->description,
+ 'coolify_details' => [
+ 'project_uuid' => $this->resource->project()->uuid,
+ 'environment_uuid' => $this->resource->environment->uuid,
+
+ 'destination_type' => $this->resource->destination_type,
+ 'destination_id' => $this->resource->destination_id,
+ 'source_type' => $this->resource->source_type,
+ 'source_id' => $this->resource->source_id,
+ 'private_key_id' => $this->resource->private_key_id,
+ ],
+
+ 'post_deployment_command' => $this->resource->post_deployment_command,
+ 'post_deployment_command_container' => $this->resource->post_deployment_command_container,
+ 'pre_deployment_command' => $this->resource->pre_deployment_command,
+ 'pre_deployment_command_container' => $this->resource->pre_deployment_command_container,
+ 'build' => [
+ 'type' => $this->resource->build_pack,
+ 'static_image' => $this->resource->static_image,
+ 'base_directory' => $this->resource->base_directory,
+ 'publish_directory' => $this->resource->publish_directory,
+ 'dockerfile' => $this->resource->dockerfile,
+ 'dockerfile_location' => $this->resource->dockerfile_location,
+ 'dockerfile_target_build' => $this->resource->dockerfile_target_build,
+ 'custom_docker_run_options' => $this->resource->custom_docker_options,
+ 'compose_parsing_version' => $this->resource->compose_parsing_version,
+ 'docker_compose' => $this->resource->docker_compose,
+ 'docker_compose_location' => $this->resource->docker_compose_location,
+ 'docker_compose_raw' => $this->resource->docker_compose_raw,
+ 'docker_compose_domains' => $this->resource->docker_compose_domains,
+ 'docker_compose_custom_start_command' => $this->resource->docker_compose_custom_start_command,
+ 'docker_compose_custom_build_command' => $this->resource->docker_compose_custom_build_command,
+ 'install_command' => $this->resource->install_command,
+ 'build_command' => $this->resource->build_command,
+ 'start_command' => $this->resource->start_command,
+ 'watch_paths' => $this->resource->watch_paths,
+ ],
+ 'source' => [
+ 'git_repository' => $this->resource->git_repository,
+ 'git_branch' => $this->resource->git_branch,
+ 'git_commit_sha' => $this->resource->git_commit_sha,
+ 'repository_project_id' => $this->resource->repository_project_id,
+ ],
+ 'docker_registry_image' => $this->getDockerRegistryImage(),
+ 'domains' => [
+ 'fqdn' => $this->resource->fqdn,
+ 'ports_exposes' => $this->resource->ports_exposes,
+ 'ports_mappings' => $this->resource->ports_mappings,
+ 'redirect' => $this->resource->redirect,
+ 'custom_nginx_configuration' => $this->resource->custom_nginx_configuration,
+ ],
+ 'environment_variables' => [
+ 'production' => $this->getEnvironmentVariables(),
+ 'preview' => $this->getPreviewEnvironmentVariables(),
+ ],
+ 'settings' => $this->getApplicationSettings(),
+ 'preview' => $this->getPreview(),
+ 'limits' => $this->resource->getLimits(),
+ 'health_check' => [
+ 'health_check_path' => $this->resource->health_check_path,
+ 'health_check_port' => $this->resource->health_check_port,
+ 'health_check_host' => $this->resource->health_check_host,
+ 'health_check_method' => $this->resource->health_check_method,
+ 'health_check_return_code' => $this->resource->health_check_return_code,
+ 'health_check_scheme' => $this->resource->health_check_scheme,
+ 'health_check_response_text' => $this->resource->health_check_response_text,
+ 'health_check_interval' => $this->resource->health_check_interval,
+ 'health_check_timeout' => $this->resource->health_check_timeout,
+ 'health_check_retries' => $this->resource->health_check_retries,
+ 'health_check_start_period' => $this->resource->health_check_start_period,
+ 'health_check_enabled' => $this->resource->health_check_enabled,
+ ],
+ 'webhooks_secrets' => [
+ 'manual_webhook_secret_github' => $this->resource->manual_webhook_secret_github,
+ 'manual_webhook_secret_gitlab' => $this->resource->manual_webhook_secret_gitlab,
+ 'manual_webhook_secret_bitbucket' => $this->resource->manual_webhook_secret_bitbucket,
+ 'manual_webhook_secret_gitea' => $this->resource->manual_webhook_secret_gitea,
+ ],
+ 'swarm' => [
+ 'swarm_replicas' => $this->resource->swarm_replicas,
+ 'swarm_placement_constraints' => $this->resource->swarm_placement_constraints,
+ ],
+ ];
+ }
+ }
+
+ protected function getPreview(): array
+ {
+ return [
+ 'preview_url_template' => $this->resource->preview_url_template,
+ ];
+ }
+
+ protected function getDockerRegistryImage(): array
+ {
+ return [
+ 'image' => $this->resource->docker_registry_image_name,
+ 'tag' => $this->resource->docker_registry_image_tag,
+ ];
+ }
+
+ protected function getEnvironmentVariables(): array
+ {
+ $variables = collect([]);
+ foreach ($this->resource->environment_variables as $env) {
+ $variables->push([
+ 'key' => $env->key,
+ 'value' => $env->value,
+ 'is_build_time' => $env->is_build_time,
+ 'is_preview' => $env->is_preview,
+ 'is_multiline' => $env->is_multiline,
+ ]);
+ }
+
+ return $variables->toArray();
+ }
+
+ protected function getPreviewEnvironmentVariables(): array
+ {
+ $variables = collect([]);
+ foreach ($this->resource->environment_variables_preview as $env) {
+ $variables->push([
+ 'key' => $env->key,
+ 'value' => $env->value,
+ 'is_build_time' => $env->is_build_time,
+ 'is_preview' => $env->is_preview,
+ 'is_multiline' => $env->is_multiline,
+ ]);
+ }
+
+ return $variables->toArray();
+ }
+
+ protected function getApplicationSettings(): array
+ {
+ $removedKeys = ['id', 'application_id', 'created_at', 'updated_at'];
+ $settings = $this->resource->settings->attributesToArray();
+ $settings = collect($settings)->filter(function ($value, $key) use ($removedKeys) {
+ return ! in_array($key, $removedKeys);
+ })->sortBy(function ($value, $key) {
+ return $key;
+ })->toArray();
+
+ return $settings;
+ }
+
+ public function saveJson(string $path): void
+ {
+ file_put_contents($path, json_encode($this->config, JSON_PRETTY_PRINT));
+ }
+
+ public function saveYaml(string $path): void
+ {
+ file_put_contents($path, Yaml::dump($this->config, 6, 2));
+ }
+
+ public function toArray(): array
+ {
+ return $this->config;
+ }
+
+ public function toJson(): string
+ {
+ return json_encode($this->config, JSON_PRETTY_PRINT);
+ }
+
+ public function toYaml(): string
+ {
+ return Yaml::dump($this->config, 6, 2);
+ }
+}
diff --git a/app/Services/DockerImageParser.php b/app/Services/DockerImageParser.php
index 4987f953d..1fd6625b3 100644
--- a/app/Services/DockerImageParser.php
+++ b/app/Services/DockerImageParser.php
@@ -43,7 +43,11 @@ public function parse(string $imageString): self
public function getFullImageNameWithoutTag(): string
{
- return $this->registryUrl.'/'.$this->imageName;
+ if ($this->registryUrl) {
+ return $this->registryUrl.'/'.$this->imageName;
+ }
+
+ return $this->imageName;
}
public function getRegistryUrl(): string
diff --git a/app/Traits/HasConfiguration.php b/app/Traits/HasConfiguration.php
new file mode 100644
index 000000000..e572c45ea
--- /dev/null
+++ b/app/Traits/HasConfiguration.php
@@ -0,0 +1,42 @@
+uuid}";
+ if (! is_dir($configDir)) {
+ mkdir($configDir, 0755, true);
+ }
+
+ $generator->saveJson($configDir.'/coolify.json');
+ $generator->saveYaml($configDir.'/coolify.yaml');
+
+ // Generate a README file with basic information
+ file_put_contents(
+ $configDir.'/README.md',
+ generate_readme_file($this->name, now()->toIso8601String())
+ );
+ }
+
+ public function getConfigurationAsJson(): string
+ {
+ return (new ConfigurationGenerator($this))->toJson();
+ }
+
+ public function getConfigurationAsYaml(): string
+ {
+ return (new ConfigurationGenerator($this))->toYaml();
+ }
+
+ public function getConfigurationAsArray(): array
+ {
+ return (new ConfigurationGenerator($this))->toArray();
+ }
+}
diff --git a/bootstrap/getHelperVersion.php b/bootstrap/getHelperVersion.php
new file mode 100644
index 000000000..766af8db2
--- /dev/null
+++ b/bootstrap/getHelperVersion.php
@@ -0,0 +1,10 @@
+startsWith('$')) {
$foundEnv = EnvironmentVariable::where([
'key' => $key,
- 'application_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
])->first();
$value = replaceVariables($value);
@@ -2653,7 +2654,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($value->startsWith('SERVICE_')) {
$foundEnv = EnvironmentVariable::where([
'key' => $key,
- 'application_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
])->first();
['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value);
if (! is_null($command)) {
@@ -2676,7 +2678,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key,
'value' => $fqdn,
'is_build_time' => false,
- 'application_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
]);
}
@@ -2687,7 +2690,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key,
'value' => $generatedValue,
'is_build_time' => false,
- 'application_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
]);
}
@@ -2712,7 +2716,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
$foundEnv = EnvironmentVariable::where([
'key' => $key,
- 'application_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
])->first();
if ($foundEnv) {
@@ -2722,7 +2727,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($foundEnv) {
$foundEnv->update([
'key' => $key,
- 'application_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_build_time' => $isBuildTime,
'value' => $defaultValue,
]);
@@ -2731,7 +2737,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key,
'value' => $defaultValue,
'is_build_time' => $isBuildTime,
- 'application_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
]);
}
@@ -2872,7 +2879,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
data_forget($service, 'volumes.*.is_directory');
data_forget($service, 'exclude_from_hc');
data_set($service, 'environment', $serviceVariables->toArray());
- updateCompose($service);
return $service;
});
diff --git a/config/constants.php b/config/constants.php
index fc2d92c56..dd8759e14 100644
--- a/config/constants.php
+++ b/config/constants.php
@@ -2,7 +2,9 @@
return [
'coolify' => [
- 'version' => '4.0.0-beta.383',
+ 'version' => '4.0.0-beta.389',
+ 'helper_version' => '1.0.5',
+ 'realtime_version' => '1.0.5',
'self_hosted' => env('SELF_HOSTED', true),
'autoupdate' => env('AUTOUPDATE'),
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
diff --git a/database/migrations/2025_01_21_125205_update_finished_at_timestamps_if_not_set.php b/database/migrations/2025_01_21_125205_update_finished_at_timestamps_if_not_set.php
new file mode 100644
index 000000000..050e8f1ae
--- /dev/null
+++ b/database/migrations/2025_01_21_125205_update_finished_at_timestamps_if_not_set.php
@@ -0,0 +1,37 @@
+whereNull('finished_at')
+ ->update(['finished_at' => DB::raw('updated_at')]);
+ } catch (\Exception $e) {
+ \Log::error('Failed to update not set finished_at timestamps for application_deployment_queues: '.$e->getMessage());
+ }
+
+ try {
+ DB::table('scheduled_database_backup_executions')
+ ->whereNull('finished_at')
+ ->update(['finished_at' => DB::raw('updated_at')]);
+ } catch (\Exception $e) {
+ \Log::error('Failed to update not set finished_at timestamps for scheduled_database_backup_executions: '.$e->getMessage());
+ }
+
+ try {
+ DB::table('scheduled_task_executions')
+ ->whereNull('finished_at')
+ ->update(['finished_at' => DB::raw('updated_at')]);
+ } catch (\Exception $e) {
+ \Log::error('Failed to update not set finished_at timestamps for scheduled_task_executions: '.$e->getMessage());
+ }
+ }
+};
diff --git a/database/migrations/2025_01_22_101105_remove_wrongly_created_envs.php b/database/migrations/2025_01_22_101105_remove_wrongly_created_envs.php
new file mode 100644
index 000000000..cb1b3cbe6
--- /dev/null
+++ b/database/migrations/2025_01_22_101105_remove_wrongly_created_envs.php
@@ -0,0 +1,19 @@
+each(function (EnvironmentVariable $environmentVariable) {
+ $environmentVariable->delete();
+ });
+ } catch (\Exception $e) {
+ Log::error('Failed to delete wrongly created environment variables: '.$e->getMessage());
+ }
+ }
+};
diff --git a/docker/coolify-helper/Dockerfile b/docker/coolify-helper/Dockerfile
index acc44d02a..bda538bca 100644
--- a/docker/coolify-helper/Dockerfile
+++ b/docker/coolify-helper/Dockerfile
@@ -1,5 +1,6 @@
# Versions
+
# https://hub.docker.com/_/alpine
ARG BASE_IMAGE=alpine:3.21
# https://download.docker.com/linux/static/stable/
@@ -11,7 +12,7 @@ ARG DOCKER_BUILDX_VERSION=0.19.3
# https://github.com/buildpacks/pack/releases
ARG PACK_VERSION=0.36.2
# https://github.com/railwayapp/nixpacks/releases
-ARG NIXPACKS_VERSION=1.30.0
+ARG NIXPACKS_VERSION=1.32.0
# https://github.com/minio/mc/releases
ARG MINIO_VERSION=RELEASE.2024-11-21T17-21-54Z
diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile
index 10bd80c2b..3032d3ef7 100644
--- a/docker/production/Dockerfile
+++ b/docker/production/Dockerfile
@@ -119,6 +119,7 @@ COPY --chown=www-data:www-data storage ./storage
COPY --chown=www-data:www-data templates ./templates
COPY --chown=www-data:www-data resources/views ./resources/views
COPY --chown=www-data:www-data artisan artisan
+COPY --chown=www-data:www-data openapi.yaml ./openapi.yaml
RUN composer dump-autoload
diff --git a/package-lock.json b/package-lock.json
index 56cfa1586..fed13e28a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,7 +22,7 @@
"pusher-js": "8.4.0-rc2",
"tailwind-scrollbar": "^3.1.0",
"tailwindcss": "3.4.17",
- "vite": "6.0.7",
+ "vite": "6.0.11",
"vue": "3.5.13"
}
},
@@ -2784,9 +2784,9 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/vite": {
- "version": "6.0.7",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz",
- "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==",
+ "version": "6.0.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz",
+ "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 7220e55a5..4e2cf6700 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"pusher-js": "8.4.0-rc2",
"tailwind-scrollbar": "^3.1.0",
"tailwindcss": "3.4.17",
- "vite": "6.0.7",
+ "vite": "6.0.11",
"vue": "3.5.13"
},
"dependencies": {
diff --git a/public/svgs/flipt.svg b/public/svgs/flipt.svg
new file mode 100644
index 000000000..8c8164f8f
--- /dev/null
+++ b/public/svgs/flipt.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/views/components/server/sidebar.blade.php b/resources/views/components/server/sidebar.blade.php
index 092e306a5..ecc5785aa 100644
--- a/resources/views/components/server/sidebar.blade.php
+++ b/resources/views/components/server/sidebar.blade.php
@@ -22,7 +22,7 @@
-
@endif
@if (!$server->isLocalhost())
diff --git a/resources/views/components/status/running.blade.php b/resources/views/components/status/running.blade.php
index 4e5f0c275..27a6d7181 100644
--- a/resources/views/components/status/running.blade.php
+++ b/resources/views/components/status/running.blade.php
@@ -6,12 +6,8 @@
])
-
-
-
-
-
-
+
+
@if ($lastDeploymentLink)
diff --git a/resources/views/livewire/project/application/configuration.blade.php b/resources/views/livewire/project/application/configuration.blade.php
index c6f27d3f3..7fcbbd691 100644
--- a/resources/views/livewire/project/application/configuration.blade.php
+++ b/resources/views/livewire/project/application/configuration.blade.php
@@ -74,8 +74,7 @@
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Resource Operations
+ href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}" >Metrics
diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php
index 62acda056..026b3b579 100644
--- a/resources/views/livewire/project/application/general.blade.php
+++ b/resources/views/livewire/project/application/general.blade.php
@@ -5,13 +5,14 @@
Save
- {{--
-
+
+ {{--
Download Config
-
+ --}}
+ {{--
-
- --}}
+ --}}
+
General configuration for your application.
diff --git a/resources/views/livewire/project/database/configuration.blade.php b/resources/views/livewire/project/database/configuration.blade.php
index 4ea3be105..cce8a5a20 100644
--- a/resources/views/livewire/project/database/configuration.blade.php
+++ b/resources/views/livewire/project/database/configuration.blade.php
@@ -20,8 +20,8 @@
href="{{ route('project.database.persistent-storage', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
wire:navigate>Persistent Storage
+ href="{{ route('project.database.import-backups', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Import
+ Backups
@@ -32,8 +32,7 @@
href="{{ route('project.database.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
wire:navigate>Resource Operations
+ href="{{ route('project.database.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Metrics
diff --git a/resources/views/livewire/project/service/index.blade.php b/resources/views/livewire/project/service/index.blade.php
index 2e31d7499..8796be966 100644
--- a/resources/views/livewire/project/service/index.blade.php
+++ b/resources/views/livewire/project/service/index.blade.php
@@ -13,7 +13,7 @@ class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-activ
@if ($serviceDatabase?->isBackupSolutionAvailable())
+ wire:navigate href="#backups">Backups
@endif
diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php
index 3be31ba55..8723f926b 100644
--- a/resources/views/livewire/project/shared/environment-variable/all.blade.php
+++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php
@@ -55,10 +55,10 @@
Preview Deployments Environment Variables
Environment (secrets) variables for Preview Deployments.
- {{-- @foreach ($resource->environment_variables_preview as $env)
+ @foreach ($resource->environment_variables_preview as $env)
- @endforeach --}}
+ @endforeach
@endif
@else
-
-
+
+ @if(!$hasShell)
+
+
+
+
+
+
+
+
Terminal Not Available
+
No shell (bash/sh) is available in this container. Please ensure either bash or sh is installed to use the terminal.
+
+
+
@else
- @if (count($containers) > 0)
- @if (count($containers) === 1)
-
- @else
-
- @endif
+ @if ($type === 'server')
+
@else
-
No containers are running.
+ @if (count($containers) === 0)
+
No containers are running.
+ @else
+ @if (count($containers) === 1)
+
+ @else
+
+ @endif
+
+
+
+ @endif
@endif
@endif
diff --git a/resources/views/livewire/project/shared/metrics.blade.php b/resources/views/livewire/project/shared/metrics.blade.php
index cfe83ded6..645de8331 100644
--- a/resources/views/livewire/project/shared/metrics.blade.php
+++ b/resources/views/livewire/project/shared/metrics.blade.php
@@ -8,7 +8,7 @@
@elseif(!$resource->destination->server->isMetricsEnabled())
Metrics are only available for servers with Sentinel & Metrics enabled!
Go to
Server settings to
+ wire:navigate href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings to
enable
it.
@else
diff --git a/resources/views/livewire/project/shared/terminal.blade.php b/resources/views/livewire/project/shared/terminal.blade.php
index 26388cc0d..edf870ca7 100644
--- a/resources/views/livewire/project/shared/terminal.blade.php
+++ b/resources/views/livewire/project/shared/terminal.blade.php
@@ -1,9 +1,4 @@
- {{--
--}}
diff --git a/resources/views/livewire/server/docker-cleanup.blade.php b/resources/views/livewire/server/docker-cleanup.blade.php
index cdf48c6aa..8151b5358 100644
--- a/resources/views/livewire/server/docker-cleanup.blade.php
+++ b/resources/views/livewire/server/docker-cleanup.blade.php
@@ -6,72 +6,75 @@
-
-
-
-
-
Docker Cleanup
-
-
-
-
- @if (!$forceDockerCleanup)
-
- @endif
+
+
+
Docker Cleanup
+
+
+
+
+ @if (!$forceDockerCleanup)
+
+ @endif
+
+
+
+
+
+ Warning: Enable these
+ options only if you fully understand their implications and
+ consequences! Improper use will result in data loss and could cause
+ functional issues.
+
-
+
Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).
+ Data from stopped containers volumes will be permanently lost.
+ No way to recover deleted volume data.
+ " />
+
-
- Warning: Enable these
- options only if you fully understand their implications and
- consequences! Improper use will result in data loss and could cause
- functional issues.
-
-
-
-
-
-
+
Recent executions (click to check output)
diff --git a/resources/views/livewire/server/proxy/deploy.blade.php b/resources/views/livewire/server/proxy/deploy.blade.php
index 85de1bee7..5be8d36a5 100644
--- a/resources/views/livewire/server/proxy/deploy.blade.php
+++ b/resources/views/livewire/server/proxy/deploy.blade.php
@@ -14,7 +14,7 @@
$traefikDashboardAvailable &&
$server->proxyType() === ProxyTypes::TRAEFIK->value)
-
+
Traefik Dashboard
diff --git a/templates/compose/affine.yaml b/templates/compose/affine.yaml
index 97be09cdb..0bac71e63 100644
--- a/templates/compose/affine.yaml
+++ b/templates/compose/affine.yaml
@@ -37,6 +37,9 @@ services:
- MAILER_USER=${MAILER_USER}
- MAILER_PASSWORD=${MAILER_PASSWORD}
- MAILER_SENDER=${MAILER_SENDER}
+ - COPILOT_FAL_API_KEY=${COPILOT_FAL_API_KEY}
+ - COPILOT_PERPLEXITY_API_KEY=${COPILOT_PERPLEXITY_API_KEY}
+ - COPILOT_OPENAI_API_KEY=${COPILOT_OPENAI_API_KEY}
healthcheck:
test: ["CMD-SHELL", "bash -c ':> /dev/tcp/127.0.0.1/3010' || exit 1"]
interval: 5s
diff --git a/templates/compose/cloudflared.yaml b/templates/compose/cloudflared.yaml
index 0d08fb24a..1769932dd 100644
--- a/templates/compose/cloudflared.yaml
+++ b/templates/compose/cloudflared.yaml
@@ -11,3 +11,8 @@ services:
command: tunnel --no-autoupdate run
environment:
- 'TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}'
+ healthcheck:
+ test: ["CMD", "cloudflared", "--version"]
+ interval: 5s
+ timeout: 20s
+ retries: 10
diff --git a/templates/compose/flipt.yaml b/templates/compose/flipt.yaml
new file mode 100644
index 000000000..74a72d9f6
--- /dev/null
+++ b/templates/compose/flipt.yaml
@@ -0,0 +1,23 @@
+# documentation: https://docs.flipt.io/cloud/overview
+# slogan: Flipt is a fully managed feature flag solution that enables you to keep your feature flags and remote config next to your code in Git.
+# tags: feature flags,devops, CI, CD
+# logo: svgs/flipt.svg
+# port: 8080
+
+services:
+ flipt:
+ image: 'docker.flipt.io/flipt/flipt:latest'
+ volumes:
+ - 'flipt-data:/var/opt/flipt'
+ environment:
+ - SERVICE_FQDN_FLIPT_8080
+ healthcheck:
+ test:
+ - CMD
+ - wget
+ - '--spider'
+ - '--quiet'
+ - 'http://127.0.0.1:8080'
+ interval: 2s
+ timeout: 10s
+ retries: 15
\ No newline at end of file
diff --git a/templates/compose/invoice-ninja.yaml b/templates/compose/invoice-ninja.yaml
index beb05d983..4ccd94b6c 100644
--- a/templates/compose/invoice-ninja.yaml
+++ b/templates/compose/invoice-ninja.yaml
@@ -9,24 +9,49 @@ services:
image: invoiceninja/invoiceninja:5
environment:
- SERVICE_FQDN_INVOICENINJA
+ - APP_NAME=${APP_NAME:-"Invoice Ninja"}
- APP_ENV=${APP_ENV:-production}
- APP_URL=${SERVICE_FQDN_INVOICENINJA}
- APP_KEY=base64:${SERVICE_REALBASE64_INVOICENINJA}
- APP_DEBUG=${APP_DEBUG:-false}
- REQUIRE_HTTPS=${REQUIRE_HTTPS:-false}
- PHANTOMJS_PDF_GENERATION=${PHANTOMJS_PDF_GENERATION:-false}
- - PDF_GENERATOR=${PDF_GENERATOR:-snappdf}
+ - PDF_GENERATOR=${PDF_GENERATOR:-hosted_ninja}
- TRUSTED_PROXIES=${TRUSTED_PROXIES:-*}
- - QUEUE_CONNECTION=${QUEUE_CONNECTION:-database}
- - IN_USER_EMAIL=${IN_USER_EMAIL:-admin@example.com}
- - IN_PASSWORD=${SERVICE_PASSWORD_INVOICENINJAUSER}
+ - CACHE_DRIVER=redis
+ - QUEUE_CONNECTION=${QUEUE_CONNECTION:-redis}
+ - SESSION_DRIVER=redis
+ - REDIS_HOST=${REDIS_HOST:-redis}
+ - REDIS_PASSWORD=${SERVICE_PASSWORD_REDIS}
+ - REDIS_PORT=${REDIS_PORT:-6379}
- DB_HOST=${DB_HOST:-mariadb}
- DB_PORT=${DB_PORT:-3306}
- DB_DATABASE=${DB_DATABASE:-invoiceninja}
- - DB_USERNAME=$SERVICE_USER_MARIADB
- - DB_PASSWORD=$SERVICE_PASSWORD_MARIADB
+ - DB_USERNAME=${SERVICE_USER_MARIADB}
+ - DB_PASSWORD=${SERVICE_PASSWORD_MARIADB}
+ - IN_USER_EMAIL=${IN_USER_EMAIL:-admin@example.com}
+ - IN_PASSWORD=${SERVICE_PASSWORD_INVOICENINJAUSER}
+ - MAIL_MAILER=${MAIL_MAILER:-log}
+ - MAIL_HOST=${MAIL_HOST}
+ - MAIL_PORT=${MAIL_PORT}
+ - MAIL_USERNAME=${MAIL_USERNAME}
+ - MAIL_PASSWORD=${MAIL_PASSWORD}
+ - MAIL_ENCRYPTION=${MAIL_ENCRYPTION}
+ - MAIL_FROM_ADDRESS=${MAIL_FROM_ADDRESS}
+ - MAIL_FROM_NAME=${MAIL_FROM_NAME}
+ - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
+ - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
+ - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}
+ - AWS_BUCKET=${AWS_BUCKET}
+ - AWS_URL=${AWS_URL}
+ - AWS_ENDPOINT=${AWS_ENDPOINT}
+ - NORDIGEN_SECRET_ID=${NORDIGEN_SECRET_ID}
+ - NORDIGEN_SECRET_KEY=${NORDIGEN_SECRET_KEY}
+ - IS_DOCKER=true
+ - SCOUT_DRIVER=${SCOUT_DRIVER}
+ - LICENSE_KEY=${LICENSE_KEY}
healthcheck:
- test: ['CMD', 'echo', 'ok']
+ test: ["CMD", "echo", "ok"]
interval: 5s
timeout: 20s
retries: 10
@@ -133,12 +158,25 @@ services:
volumes:
- mariadb-data:/var/lib/mysql
environment:
- - MYSQL_ROOT_PASSWORD=$SERVICE_PASSWORD_MARIADBROOT
+ - MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MARIADBROOT}
- MYSQL_DATABASE=${DB_DATABASE:-invoiceninja}
- - MYSQL_USER=$SERVICE_USER_MARIADB
- - MYSQL_PASSWORD=$SERVICE_PASSWORD_MARIADB
+ - MYSQL_USER=${SERVICE_USER_MARIADB}
+ - MYSQL_PASSWORD=${SERVICE_PASSWORD_MARIADB}
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 5s
timeout: 20s
retries: 10
+
+ redis:
+ image: "redis:7.4-alpine"
+ command: redis-server --requirepass ${SERVICE_PASSWORD_REDIS}
+ environment:
+ - REDIS_PASSWORD=${SERVICE_PASSWORD_REDIS}
+ volumes:
+ - "invoice-ninja-redis-data:/data"
+ healthcheck:
+ test: ["CMD", "redis-cli", "-a", "${SERVICE_PASSWORD_REDIS}", "ping"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
diff --git a/templates/compose/plunk.yaml b/templates/compose/plunk.yaml
index 4b356720a..60c2ee850 100644
--- a/templates/compose/plunk.yaml
+++ b/templates/compose/plunk.yaml
@@ -25,9 +25,10 @@ services:
- APP_URI=${SERVICE_FQDN_PLUNK}
- API_URI=${SERVICE_FQDN_PLUNK}/api
- DISABLE_SIGNUPS=${DISABLE_SIGNUPS:-False}
+ - NODE_OPTIONS=--no-network-family-autoselection
entrypoint: [ "/app/entry.sh" ]
healthcheck:
- test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000"]
+ test: ["CMD-SHELL", "(wget -S --spider http://127.0.0.1:3000/api/health 2>&1 | grep -q \"HTTP/1.1 [1-3]\")"]
interval: 2s
timeout: 10s
retries: 15
diff --git a/templates/service-templates.json b/templates/service-templates.json
index c703f9bdc..2c2ed2e54 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -33,7 +33,7 @@
"affine": {
"documentation": "https://docs.affine.pro/docs/self-host-affine?utm_source=coolify.io",
"slogan": "Affine is an open-source, all-in-one workspace and OS for knowledge management, a Notion/Miro alternative.",
- "compose": "c2VydmljZXM6CiAgYWZmaW5lOgogICAgaW1hZ2U6ICdnaGNyLmlvL3RvZXZlcnl0aGluZy9hZmZpbmUtZ3JhcGhxbDpzdGFibGUnCiAgICBjb21tYW5kOgogICAgICAtIHNoCiAgICAgIC0gJy1jJwogICAgICAtICdub2RlIC4vc2NyaXB0cy9zZWxmLWhvc3QtcHJlZGVwbG95ICYmIG5vZGUgLi9kaXN0L2luZGV4LmpzJwogICAgZGVwZW5kc19vbjoKICAgICAgcmVkaXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FmZmluZS1jb25maWc6L3Jvb3QvLmFmZmluZS9jb25maWcnCiAgICAgIC0gJ2FmZmluZS1zdG9yYWdlOi9yb290Ly5hZmZpbmUvc3RvcmFnZScKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LXNpemU6IDEwMDBtCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQUZGSU5FXzMwMTAKICAgICAgLSBOT0RFX09QVElPTlM9LS1pbXBvcnQ9Li9zY3JpcHRzL3JlZ2lzdGVyLmpzCiAgICAgIC0gQUZGSU5FX0NPTkZJR19QQVRIPS9yb290Ly5hZmZpbmUvY29uZmlnCiAgICAgIC0gUkVESVNfU0VSVkVSX0hPU1Q9cmVkaXMKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTX0RCOi1hZmZpbmV9JwogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSBBRkZJTkVfU0VSVkVSX0hPU1Q9JFNFUlZJQ0VfRlFETl9BRkZJTkUKICAgICAgLSBBRkZJTkVfU0VSVkVSX0VYVEVSTkFMX1VSTD0kU0VSVklDRV9GUUROX0FGRklORQogICAgICAtICdNQUlMRVJfSE9TVD0ke01BSUxFUl9IT1NUfScKICAgICAgLSAnTUFJTEVSX1BPUlQ9JHtNQUlMRVJfUE9SVH0nCiAgICAgIC0gJ01BSUxFUl9VU0VSPSR7TUFJTEVSX1VTRVJ9JwogICAgICAtICdNQUlMRVJfUEFTU1dPUkQ9JHtNQUlMRVJfUEFTU1dPUkR9JwogICAgICAtICdNQUlMRVJfU0VOREVSPSR7TUFJTEVSX1NFTkRFUn0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gImJhc2ggLWMgJzo+IC9kZXYvdGNwLzEyNy4wLjAuMS8zMDEwJyB8fCBleGl0IDEiCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMwogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgICB2b2x1bWVzOgogICAgICAtICdhZmZpbmUtcmVkaXMtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtICctLXJhdycKICAgICAgICAtIGluY3IKICAgICAgICAtIHBpbmcKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiA1CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2JwogICAgdm9sdW1lczoKICAgICAgLSAnYWZmaW5lLXBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VIGFmZmluZScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiA1CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWFmZmluZX0nCiAgICAgIC0gUEdEQVRBPS92YXIvbGliL3Bvc3RncmVzcWwvZGF0YS9wZ2RhdGEK",
+ "compose": "c2VydmljZXM6CiAgYWZmaW5lOgogICAgaW1hZ2U6ICdnaGNyLmlvL3RvZXZlcnl0aGluZy9hZmZpbmUtZ3JhcGhxbDpzdGFibGUnCiAgICBjb21tYW5kOgogICAgICAtIHNoCiAgICAgIC0gJy1jJwogICAgICAtICdub2RlIC4vc2NyaXB0cy9zZWxmLWhvc3QtcHJlZGVwbG95ICYmIG5vZGUgLi9kaXN0L2luZGV4LmpzJwogICAgZGVwZW5kc19vbjoKICAgICAgcmVkaXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FmZmluZS1jb25maWc6L3Jvb3QvLmFmZmluZS9jb25maWcnCiAgICAgIC0gJ2FmZmluZS1zdG9yYWdlOi9yb290Ly5hZmZpbmUvc3RvcmFnZScKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LXNpemU6IDEwMDBtCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQUZGSU5FXzMwMTAKICAgICAgLSBOT0RFX09QVElPTlM9LS1pbXBvcnQ9Li9zY3JpcHRzL3JlZ2lzdGVyLmpzCiAgICAgIC0gQUZGSU5FX0NPTkZJR19QQVRIPS9yb290Ly5hZmZpbmUvY29uZmlnCiAgICAgIC0gUkVESVNfU0VSVkVSX0hPU1Q9cmVkaXMKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTX0RCOi1hZmZpbmV9JwogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSBBRkZJTkVfU0VSVkVSX0hPU1Q9JFNFUlZJQ0VfRlFETl9BRkZJTkUKICAgICAgLSBBRkZJTkVfU0VSVkVSX0VYVEVSTkFMX1VSTD0kU0VSVklDRV9GUUROX0FGRklORQogICAgICAtICdNQUlMRVJfSE9TVD0ke01BSUxFUl9IT1NUfScKICAgICAgLSAnTUFJTEVSX1BPUlQ9JHtNQUlMRVJfUE9SVH0nCiAgICAgIC0gJ01BSUxFUl9VU0VSPSR7TUFJTEVSX1VTRVJ9JwogICAgICAtICdNQUlMRVJfUEFTU1dPUkQ9JHtNQUlMRVJfUEFTU1dPUkR9JwogICAgICAtICdNQUlMRVJfU0VOREVSPSR7TUFJTEVSX1NFTkRFUn0nCiAgICAgIC0gJ0NPUElMT1RfRkFMX0FQSV9LRVk9JHtDT1BJTE9UX0ZBTF9BUElfS0VZfScKICAgICAgLSAnQ09QSUxPVF9QRVJQTEVYSVRZX0FQSV9LRVk9JHtDT1BJTE9UX1BFUlBMRVhJVFlfQVBJX0tFWX0nCiAgICAgIC0gJ0NPUElMT1RfT1BFTkFJX0FQSV9LRVk9JHtDT1BJTE9UX09QRU5BSV9BUElfS0VZfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAiYmFzaCAtYyAnOj4gL2Rldi90Y3AvMTI3LjAuMC4xLzMwMTAnIHx8IGV4aXQgMSIKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAzCiAgcmVkaXM6CiAgICBpbWFnZTogcmVkaXMKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FmZmluZS1yZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gJy0tcmF3JwogICAgICAgIC0gaW5jcgogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYnCiAgICB2b2x1bWVzOgogICAgICAtICdhZmZpbmUtcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgYWZmaW5lJwogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotYWZmaW5lfScKICAgICAgLSBQR0RBVEE9L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhL3BnZGF0YQo=",
"tags": [
"knowledge-management",
"notion",
@@ -449,7 +449,7 @@
"cloudflared": {
"documentation": "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/?utm_source=coolify.io",
"slogan": "Client for Cloudflare Tunnel, a daemon that exposes private services through the Cloudflare edge.",
- "compose": "c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya19tb2RlOiBob3N0CiAgICBjb21tYW5kOiAndHVubmVsIC0tbm8tYXV0b3VwZGF0ZSBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnVFVOTkVMX1RPS0VOPSR7Q0xPVURGTEFSRV9UVU5ORUxfVE9LRU59Jwo=",
+ "compose": "c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya19tb2RlOiBob3N0CiAgICBjb21tYW5kOiAndHVubmVsIC0tbm8tYXV0b3VwZGF0ZSBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnVFVOTkVMX1RPS0VOPSR7Q0xPVURGTEFSRV9UVU5ORUxfVE9LRU59JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGNsb3VkZmxhcmVkCiAgICAgICAgLSAnLS12ZXJzaW9uJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
"tags": null,
"logo": "svgs/cloudflared.svg",
"minversion": "0.0.0"
@@ -832,6 +832,20 @@
"minversion": "0.0.0",
"port": "5800"
},
+ "flipt": {
+ "documentation": "https://docs.flipt.io/cloud/overview?utm_source=coolify.io",
+ "slogan": "Flipt is a fully managed feature flag solution that enables you to keep your feature flags and remote config next to your code in Git.",
+ "compose": "c2VydmljZXM6CiAgZmxpcHQ6CiAgICBpbWFnZTogJ2RvY2tlci5mbGlwdC5pby9mbGlwdC9mbGlwdDpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdmbGlwdC1kYXRhOi92YXIvb3B0L2ZsaXB0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0ZMSVBUXzgwODAKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnLS1xdWlldCcKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "tags": [
+ "feature flags",
+ "devops",
+ "ci",
+ "cd"
+ ],
+ "logo": "svgs/flipt.svg",
+ "minversion": "0.0.0",
+ "port": "8080"
+ },
"flowise-with-databases": {
"documentation": "https://docs.flowiseai.com/?utm_source=coolify.io",
"slogan": "Flowise is an open source low-code tool for developers to build customized LLM orchestration flows & AI agents. Also deploys Redis, Postgres and other services.",
@@ -1367,7 +1381,7 @@
"invoice-ninja": {
"documentation": "https://invoiceninja.github.io/selfhost.html?utm_source=coolify.io",
"slogan": "The leading open-source invoicing platform",
- "compose": "c2VydmljZXM6CiAgaW52b2ljZS1uaW5qYToKICAgIGltYWdlOiAnaW52b2ljZW5pbmphL2ludm9pY2VuaW5qYTo1JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0lOVk9JQ0VOSU5KQQogICAgICAtICdBUFBfRU5WPSR7QVBQX0VOVjotcHJvZHVjdGlvbn0nCiAgICAgIC0gJ0FQUF9VUkw9JHtTRVJWSUNFX0ZRRE5fSU5WT0lDRU5JTkpBfScKICAgICAgLSAnQVBQX0tFWT1iYXNlNjQ6JHtTRVJWSUNFX1JFQUxCQVNFNjRfSU5WT0lDRU5JTkpBfScKICAgICAgLSAnQVBQX0RFQlVHPSR7QVBQX0RFQlVHOi1mYWxzZX0nCiAgICAgIC0gJ1JFUVVJUkVfSFRUUFM9JHtSRVFVSVJFX0hUVFBTOi1mYWxzZX0nCiAgICAgIC0gJ1BIQU5UT01KU19QREZfR0VORVJBVElPTj0ke1BIQU5UT01KU19QREZfR0VORVJBVElPTjotZmFsc2V9JwogICAgICAtICdQREZfR0VORVJBVE9SPSR7UERGX0dFTkVSQVRPUjotc25hcHBkZn0nCiAgICAgIC0gJ1RSVVNURURfUFJPWElFUz0ke1RSVVNURURfUFJPWElFUzotKn0nCiAgICAgIC0gJ1FVRVVFX0NPTk5FQ1RJT049JHtRVUVVRV9DT05ORUNUSU9OOi1kYXRhYmFzZX0nCiAgICAgIC0gJ0lOX1VTRVJfRU1BSUw9JHtJTl9VU0VSX0VNQUlMOi1hZG1pbkBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0lOX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9JTlZPSUNFTklOSkFVU0VSfScKICAgICAgLSAnREJfSE9TVD0ke0RCX0hPU1Q6LW1hcmlhZGJ9JwogICAgICAtICdEQl9QT1JUPSR7REJfUE9SVDotMzMwNn0nCiAgICAgIC0gJ0RCX0RBVEFCQVNFPSR7REJfREFUQUJBU0U6LWludm9pY2VuaW5qYX0nCiAgICAgIC0gREJfVVNFUk5BTUU9JFNFUlZJQ0VfVVNFUl9NQVJJQURCCiAgICAgIC0gREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQgogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGVjaG8KICAgICAgICAtIG9rCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2ludm9pY2UtbmluamEtcHVibGljOi92YXIvd3d3L2FwcC9wdWJsaWMnCiAgICAgIC0gJ2ludm9pY2UtbmluamEtc3RvcmFnZTovdmFyL3d3dy9hcHAvc3RvcmFnZScKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vc3VwZXJ2aXNvcmQuY29uZgogICAgICAgIHRhcmdldDogL2V0Yy9zdXBlcnZpc29yZC5jb25mCiAgICAgICAgY29udGVudDogIltzdXBlcnZpc29yZF1cbm5vZGFlbW9uPXRydWVcbnBpZGZpbGU9L3RtcC9zdXBlcnZpc29yZC5waWRcbmxvZ2ZpbGU9L2Rldi9udWxsIDsgbm9kYWVtb24gd2lsbCBjYXVzZSBsb2dzIHRvIGdvIHRvIHN0ZG91dFxubG9nZmlsZV9tYXhieXRlcz0wXG5sb2dsZXZlbD1pbmZvXG5cbltwcm9ncmFtOnBocC1mcG1dXG5yZWRpcmVjdF9zdGRlcnI9dHJ1ZVxuc3Rkb3V0X2xvZ2ZpbGU9L2Rldi9zdGRvdXRcbnN0ZG91dF9sb2dmaWxlX21heGJ5dGVzPTBcbnN0ZGVycl9sb2dmaWxlPS9kZXYvc3RkZXJyXG5zdGRlcnJfbG9nZmlsZV9tYXhieXRlcz0wXG5jb21tYW5kPXBocCBhcnRpc2FuIHNlcnZlIC0taG9zdCAwLjAuMC4wIC0tcG9ydCA5MDAwXG5cbltwcm9ncmFtOnNjaGVkdWxlcl1cbmF1dG9yZXN0YXJ0PXRydWVcbnJlZGlyZWN0X3N0ZGVycj10cnVlXG5zdGRvdXRfbG9nZmlsZT0vZGV2L3N0ZG91dFxuc3Rkb3V0X2xvZ2ZpbGVfbWF4Ynl0ZXM9MFxuc3RkZXJyX2xvZ2ZpbGU9L2Rldi9zdGRlcnJcbnN0ZGVycl9sb2dmaWxlX21heGJ5dGVzPTBcbmNvbW1hbmQ9cGhwIGFydGlzYW4gc2NoZWR1bGU6d29ya1xuXG5bcHJvZ3JhbTpxdWV1ZS13b3JrZXJdXG5wcm9jZXNzX25hbWU9JShwcm9ncmFtX25hbWUpc18lKHByb2Nlc3NfbnVtKTAyZFxuYXV0b3Jlc3RhcnQ9dHJ1ZVxucmVkaXJlY3Rfc3RkZXJyPXRydWVcbnN0ZG91dF9sb2dmaWxlPS9kZXYvc3Rkb3V0XG5zdGRvdXRfbG9nZmlsZV9tYXhieXRlcz0wXG5zdGRlcnJfbG9nZmlsZT0vZGV2L3N0ZGVyclxuc3RkZXJyX2xvZ2ZpbGVfbWF4Ynl0ZXM9MFxubnVtcHJvY3M9MlxuY29tbWFuZD1waHAgYXJ0aXNhbiBxdWV1ZTp3b3JrIC0tc2xlZXA9MyAtLXRyaWVzPTEgLS1tZW1vcnk9MjU2IC0tdGltZW91dD0zNjAwXG5cbltldmVudGxpc3RlbmVyOnNodXRkb3duXVxuY29tbWFuZD1zaHV0ZG93bi5zaFxuZXZlbnRzPVBST0NFU1NfU1RBVEVfU1RPUFBFRCwgUFJPQ0VTU19TVEFURV9FWElURUQsIFBST0NFU1NfU1RBVEVfRkFUQUxcbnN0ZG91dF9sb2dmaWxlPS9kZXYvc3Rkb3V0XG5zdGRvdXRfbG9nZmlsZV9tYXhieXRlcz0wXG5zdGRlcnJfbG9nZmlsZT0vZGV2L3N0ZGVyclxuc3RkZXJyX2xvZ2ZpbGVfbWF4Ynl0ZXM9MFxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9waHAuaW5pCiAgICAgICAgdGFyZ2V0OiAvdXNyL2xvY2FsL2V0Yy9waHAvcGhwLmluaQogICAgICAgIGNvbnRlbnQ6ICJzZXNzaW9uLmF1dG9fc3RhcnQgPSBPZmZcbnNob3J0X29wZW5fdGFnID0gT2ZmXG5cbmVycm9yX3JlcG9ydGluZyA9IEVfQUxMICYgfkVfTk9USUNFICYgfkVfV0FSTklORyAmIH5FX1NUUklDVCAmIH5FX0RFUFJFQ0FURURcblxuOyBvcGNhY2hlLmVuYWJsZT0xXG47IG9wY2FjaGUucHJlbG9hZD0vc3J2L3d3dy9pbnZvaWNlbmluamEvY3VycmVudC9wcmVsb2FkLnBocFxuOyBvcGNhY2hlLnByZWxvYWRfdXNlcj13d3ctZGF0YVxuXG47IDsgVGhlIE9QY2FjaGUgc2hhcmVkIG1lbW9yeSBzdG9yYWdlIHNpemUuXG47IG9wY2FjaGUubWF4X2FjY2VsZXJhdGVkX2ZpbGVzPTMwMDAwMFxuOyBvcGNhY2hlLnZhbGlkYXRlX3RpbWVzdGFtcHM9MVxuOyBvcGNhY2hlLnJldmFsaWRhdGVfZnJlcT0zMFxuOyBvcGNhY2hlLmppdF9idWZmZXJfc2l6ZT0yNTZNXG47IG9wY2FjaGUuaml0PTEyMDVcbjsgb3BjYWNoZS5tZW1vcnlfY29uc3VtcHRpb249MTAyNE1cblxucG9zdF9tYXhfc2l6ZSA9IDYwTVxudXBsb2FkX21heF9maWxlc2l6ZSA9IDUwTVxubWVtb3J5X2xpbWl0PTUxMk1cbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vcGhwLWNsaS5pbmkKICAgICAgICB0YXJnZXQ6IC91c3IvbG9jYWwvZXRjL3BocC9waHAtY2xpLmluaQogICAgICAgIGNvbnRlbnQ6ICJzZXNzaW9uLmF1dG9fc3RhcnQgPSBPZmZcbnNob3J0X29wZW5fdGFnID0gT2ZmXG5cbmVycm9yX3JlcG9ydGluZyA9IEVfQUxMICYgfkVfTk9USUNFICYgfkVfV0FSTklORyAmIH5FX1NUUklDVCAmIH5FX0RFUFJFQ0FURURcblxuOyBvcGNhY2hlLmVuYWJsZV9jbGk9MVxuOyBvcGNhY2hlLmZhc3Rfc2h1dGRvd249MVxuOyBvcGNhY2hlLm1lbW9yeV9jb25zdW1wdGlvbj0yNTZcbjsgb3BjYWNoZS5pbnRlcm5lZF9zdHJpbmdzX2J1ZmZlcj04XG47IG9wY2FjaGUubWF4X2FjY2VsZXJhdGVkX2ZpbGVzPTQwMDBcbjsgb3BjYWNoZS5yZXZhbGlkYXRlX2ZyZXE9NjBcbjsgIyBodHRwOi8vc3ltZm9ueS5jb20vZG9jL2N1cnJlbnQvcGVyZm9ybWFuY2UuaHRtbFxuOyByZWFscGF0aF9jYWNoZV9zaXplID0gNDA5NktcbjsgcmVhbHBhdGhfY2FjaGVfdHRsID0gNjAwXG5cbm1lbW9yeV9saW1pdCA9IDJHXG5wb3N0X21heF9zaXplID0gNjBNXG51cGxvYWRfbWF4X2ZpbGVzaXplID0gNTBNIgogICAgZGVwZW5kc19vbjoKICAgICAgbWFyaWFkYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG1hcmlhZGI6CiAgICBpbWFnZTogJ21hcmlhZGI6MTEnCiAgICB2b2x1bWVzOgogICAgICAtICdtYXJpYWRiLWRhdGE6L3Zhci9saWIvbXlzcWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBNWVNRTF9ST09UX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX01BUklBREJST09UCiAgICAgIC0gJ01ZU1FMX0RBVEFCQVNFPSR7REJfREFUQUJBU0U6LWludm9pY2VuaW5qYX0nCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX01BUklBREIKICAgICAgLSBNWVNRTF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NQVJJQURCCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gaGVhbHRoY2hlY2suc2gKICAgICAgICAtICctLWNvbm5lY3QnCiAgICAgICAgLSAnLS1pbm5vZGJfaW5pdGlhbGl6ZWQnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
+ "compose": "c2VydmljZXM6CiAgaW52b2ljZS1uaW5qYToKICAgIGltYWdlOiAnaW52b2ljZW5pbmphL2ludm9pY2VuaW5qYTo1JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0lOVk9JQ0VOSU5KQQogICAgICAtICdBUFBfTkFNRT0ke0FQUF9OQU1FOi0iSW52b2ljZSBOaW5qYSJ9JwogICAgICAtICdBUFBfRU5WPSR7QVBQX0VOVjotcHJvZHVjdGlvbn0nCiAgICAgIC0gJ0FQUF9VUkw9JHtTRVJWSUNFX0ZRRE5fSU5WT0lDRU5JTkpBfScKICAgICAgLSAnQVBQX0tFWT1iYXNlNjQ6JHtTRVJWSUNFX1JFQUxCQVNFNjRfSU5WT0lDRU5JTkpBfScKICAgICAgLSAnQVBQX0RFQlVHPSR7QVBQX0RFQlVHOi1mYWxzZX0nCiAgICAgIC0gJ1JFUVVJUkVfSFRUUFM9JHtSRVFVSVJFX0hUVFBTOi1mYWxzZX0nCiAgICAgIC0gJ1BIQU5UT01KU19QREZfR0VORVJBVElPTj0ke1BIQU5UT01KU19QREZfR0VORVJBVElPTjotZmFsc2V9JwogICAgICAtICdQREZfR0VORVJBVE9SPSR7UERGX0dFTkVSQVRPUjotaG9zdGVkX25pbmphfScKICAgICAgLSAnVFJVU1RFRF9QUk9YSUVTPSR7VFJVU1RFRF9QUk9YSUVTOi0qfScKICAgICAgLSBDQUNIRV9EUklWRVI9cmVkaXMKICAgICAgLSAnUVVFVUVfQ09OTkVDVElPTj0ke1FVRVVFX0NPTk5FQ1RJT046LXJlZGlzfScKICAgICAgLSBTRVNTSU9OX0RSSVZFUj1yZWRpcwogICAgICAtICdSRURJU19IT1NUPSR7UkVESVNfSE9TVDotcmVkaXN9JwogICAgICAtICdSRURJU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgICAtICdSRURJU19QT1JUPSR7UkVESVNfUE9SVDotNjM3OX0nCiAgICAgIC0gJ0RCX0hPU1Q9JHtEQl9IT1NUOi1tYXJpYWRifScKICAgICAgLSAnREJfUE9SVD0ke0RCX1BPUlQ6LTMzMDZ9JwogICAgICAtICdEQl9EQVRBQkFTRT0ke0RCX0RBVEFCQVNFOi1pbnZvaWNlbmluamF9JwogICAgICAtICdEQl9VU0VSTkFNRT0ke1NFUlZJQ0VfVVNFUl9NQVJJQURCfScKICAgICAgLSAnREJfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01BUklBREJ9JwogICAgICAtICdJTl9VU0VSX0VNQUlMPSR7SU5fVVNFUl9FTUFJTDotYWRtaW5AZXhhbXBsZS5jb219JwogICAgICAtICdJTl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfSU5WT0lDRU5JTkpBVVNFUn0nCiAgICAgIC0gJ01BSUxfTUFJTEVSPSR7TUFJTF9NQUlMRVI6LWxvZ30nCiAgICAgIC0gJ01BSUxfSE9TVD0ke01BSUxfSE9TVH0nCiAgICAgIC0gJ01BSUxfUE9SVD0ke01BSUxfUE9SVH0nCiAgICAgIC0gJ01BSUxfVVNFUk5BTUU9JHtNQUlMX1VTRVJOQU1FfScKICAgICAgLSAnTUFJTF9QQVNTV09SRD0ke01BSUxfUEFTU1dPUkR9JwogICAgICAtICdNQUlMX0VOQ1JZUFRJT049JHtNQUlMX0VOQ1JZUFRJT059JwogICAgICAtICdNQUlMX0ZST01fQUREUkVTUz0ke01BSUxfRlJPTV9BRERSRVNTfScKICAgICAgLSAnTUFJTF9GUk9NX05BTUU9JHtNQUlMX0ZST01fTkFNRX0nCiAgICAgIC0gJ0FXU19BQ0NFU1NfS0VZX0lEPSR7QVdTX0FDQ0VTU19LRVlfSUR9JwogICAgICAtICdBV1NfU0VDUkVUX0FDQ0VTU19LRVk9JHtBV1NfU0VDUkVUX0FDQ0VTU19LRVl9JwogICAgICAtICdBV1NfREVGQVVMVF9SRUdJT049JHtBV1NfREVGQVVMVF9SRUdJT059JwogICAgICAtICdBV1NfQlVDS0VUPSR7QVdTX0JVQ0tFVH0nCiAgICAgIC0gJ0FXU19VUkw9JHtBV1NfVVJMfScKICAgICAgLSAnQVdTX0VORFBPSU5UPSR7QVdTX0VORFBPSU5UfScKICAgICAgLSAnTk9SRElHRU5fU0VDUkVUX0lEPSR7Tk9SRElHRU5fU0VDUkVUX0lEfScKICAgICAgLSAnTk9SRElHRU5fU0VDUkVUX0tFWT0ke05PUkRJR0VOX1NFQ1JFVF9LRVl9JwogICAgICAtIElTX0RPQ0tFUj10cnVlCiAgICAgIC0gJ1NDT1VUX0RSSVZFUj0ke1NDT1VUX0RSSVZFUn0nCiAgICAgIC0gJ0xJQ0VOU0VfS0VZPSR7TElDRU5TRV9LRVl9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGVjaG8KICAgICAgICAtIG9rCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2ludm9pY2UtbmluamEtcHVibGljOi92YXIvd3d3L2FwcC9wdWJsaWMnCiAgICAgIC0gJ2ludm9pY2UtbmluamEtc3RvcmFnZTovdmFyL3d3dy9hcHAvc3RvcmFnZScKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vc3VwZXJ2aXNvcmQuY29uZgogICAgICAgIHRhcmdldDogL2V0Yy9zdXBlcnZpc29yZC5jb25mCiAgICAgICAgY29udGVudDogIltzdXBlcnZpc29yZF1cbm5vZGFlbW9uPXRydWVcbnBpZGZpbGU9L3RtcC9zdXBlcnZpc29yZC5waWRcbmxvZ2ZpbGU9L2Rldi9udWxsIDsgbm9kYWVtb24gd2lsbCBjYXVzZSBsb2dzIHRvIGdvIHRvIHN0ZG91dFxubG9nZmlsZV9tYXhieXRlcz0wXG5sb2dsZXZlbD1pbmZvXG5cbltwcm9ncmFtOnBocC1mcG1dXG5yZWRpcmVjdF9zdGRlcnI9dHJ1ZVxuc3Rkb3V0X2xvZ2ZpbGU9L2Rldi9zdGRvdXRcbnN0ZG91dF9sb2dmaWxlX21heGJ5dGVzPTBcbnN0ZGVycl9sb2dmaWxlPS9kZXYvc3RkZXJyXG5zdGRlcnJfbG9nZmlsZV9tYXhieXRlcz0wXG5jb21tYW5kPXBocCBhcnRpc2FuIHNlcnZlIC0taG9zdCAwLjAuMC4wIC0tcG9ydCA5MDAwXG5cbltwcm9ncmFtOnNjaGVkdWxlcl1cbmF1dG9yZXN0YXJ0PXRydWVcbnJlZGlyZWN0X3N0ZGVycj10cnVlXG5zdGRvdXRfbG9nZmlsZT0vZGV2L3N0ZG91dFxuc3Rkb3V0X2xvZ2ZpbGVfbWF4Ynl0ZXM9MFxuc3RkZXJyX2xvZ2ZpbGU9L2Rldi9zdGRlcnJcbnN0ZGVycl9sb2dmaWxlX21heGJ5dGVzPTBcbmNvbW1hbmQ9cGhwIGFydGlzYW4gc2NoZWR1bGU6d29ya1xuXG5bcHJvZ3JhbTpxdWV1ZS13b3JrZXJdXG5wcm9jZXNzX25hbWU9JShwcm9ncmFtX25hbWUpc18lKHByb2Nlc3NfbnVtKTAyZFxuYXV0b3Jlc3RhcnQ9dHJ1ZVxucmVkaXJlY3Rfc3RkZXJyPXRydWVcbnN0ZG91dF9sb2dmaWxlPS9kZXYvc3Rkb3V0XG5zdGRvdXRfbG9nZmlsZV9tYXhieXRlcz0wXG5zdGRlcnJfbG9nZmlsZT0vZGV2L3N0ZGVyclxuc3RkZXJyX2xvZ2ZpbGVfbWF4Ynl0ZXM9MFxubnVtcHJvY3M9MlxuY29tbWFuZD1waHAgYXJ0aXNhbiBxdWV1ZTp3b3JrIC0tc2xlZXA9MyAtLXRyaWVzPTEgLS1tZW1vcnk9MjU2IC0tdGltZW91dD0zNjAwXG5cbltldmVudGxpc3RlbmVyOnNodXRkb3duXVxuY29tbWFuZD1zaHV0ZG93bi5zaFxuZXZlbnRzPVBST0NFU1NfU1RBVEVfU1RPUFBFRCwgUFJPQ0VTU19TVEFURV9FWElURUQsIFBST0NFU1NfU1RBVEVfRkFUQUxcbnN0ZG91dF9sb2dmaWxlPS9kZXYvc3Rkb3V0XG5zdGRvdXRfbG9nZmlsZV9tYXhieXRlcz0wXG5zdGRlcnJfbG9nZmlsZT0vZGV2L3N0ZGVyclxuc3RkZXJyX2xvZ2ZpbGVfbWF4Ynl0ZXM9MFxuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9waHAuaW5pCiAgICAgICAgdGFyZ2V0OiAvdXNyL2xvY2FsL2V0Yy9waHAvcGhwLmluaQogICAgICAgIGNvbnRlbnQ6ICJzZXNzaW9uLmF1dG9fc3RhcnQgPSBPZmZcbnNob3J0X29wZW5fdGFnID0gT2ZmXG5cbmVycm9yX3JlcG9ydGluZyA9IEVfQUxMICYgfkVfTk9USUNFICYgfkVfV0FSTklORyAmIH5FX1NUUklDVCAmIH5FX0RFUFJFQ0FURURcblxuOyBvcGNhY2hlLmVuYWJsZT0xXG47IG9wY2FjaGUucHJlbG9hZD0vc3J2L3d3dy9pbnZvaWNlbmluamEvY3VycmVudC9wcmVsb2FkLnBocFxuOyBvcGNhY2hlLnByZWxvYWRfdXNlcj13d3ctZGF0YVxuXG47IDsgVGhlIE9QY2FjaGUgc2hhcmVkIG1lbW9yeSBzdG9yYWdlIHNpemUuXG47IG9wY2FjaGUubWF4X2FjY2VsZXJhdGVkX2ZpbGVzPTMwMDAwMFxuOyBvcGNhY2hlLnZhbGlkYXRlX3RpbWVzdGFtcHM9MVxuOyBvcGNhY2hlLnJldmFsaWRhdGVfZnJlcT0zMFxuOyBvcGNhY2hlLmppdF9idWZmZXJfc2l6ZT0yNTZNXG47IG9wY2FjaGUuaml0PTEyMDVcbjsgb3BjYWNoZS5tZW1vcnlfY29uc3VtcHRpb249MTAyNE1cblxucG9zdF9tYXhfc2l6ZSA9IDYwTVxudXBsb2FkX21heF9maWxlc2l6ZSA9IDUwTVxubWVtb3J5X2xpbWl0PTUxMk1cbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vcGhwLWNsaS5pbmkKICAgICAgICB0YXJnZXQ6IC91c3IvbG9jYWwvZXRjL3BocC9waHAtY2xpLmluaQogICAgICAgIGNvbnRlbnQ6ICJzZXNzaW9uLmF1dG9fc3RhcnQgPSBPZmZcbnNob3J0X29wZW5fdGFnID0gT2ZmXG5cbmVycm9yX3JlcG9ydGluZyA9IEVfQUxMICYgfkVfTk9USUNFICYgfkVfV0FSTklORyAmIH5FX1NUUklDVCAmIH5FX0RFUFJFQ0FURURcblxuOyBvcGNhY2hlLmVuYWJsZV9jbGk9MVxuOyBvcGNhY2hlLmZhc3Rfc2h1dGRvd249MVxuOyBvcGNhY2hlLm1lbW9yeV9jb25zdW1wdGlvbj0yNTZcbjsgb3BjYWNoZS5pbnRlcm5lZF9zdHJpbmdzX2J1ZmZlcj04XG47IG9wY2FjaGUubWF4X2FjY2VsZXJhdGVkX2ZpbGVzPTQwMDBcbjsgb3BjYWNoZS5yZXZhbGlkYXRlX2ZyZXE9NjBcbjsgIyBodHRwOi8vc3ltZm9ueS5jb20vZG9jL2N1cnJlbnQvcGVyZm9ybWFuY2UuaHRtbFxuOyByZWFscGF0aF9jYWNoZV9zaXplID0gNDA5NktcbjsgcmVhbHBhdGhfY2FjaGVfdHRsID0gNjAwXG5cbm1lbW9yeV9saW1pdCA9IDJHXG5wb3N0X21heF9zaXplID0gNjBNXG51cGxvYWRfbWF4X2ZpbGVzaXplID0gNTBNXG4iCiAgICBkZXBlbmRzX29uOgogICAgICBtYXJpYWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21hcmlhZGItZGF0YTovdmFyL2xpYi9teXNxbCcKICAgIGVudmlyb25tZW50OgogICAgICAtICdNWVNRTF9ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NQVJJQURCUk9PVH0nCiAgICAgIC0gJ01ZU1FMX0RBVEFCQVNFPSR7REJfREFUQUJBU0U6LWludm9pY2VuaW5qYX0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUFSSUFEQn0nCiAgICAgIC0gJ01ZU1FMX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NQVJJQURCfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBoZWFsdGhjaGVjay5zaAogICAgICAgIC0gJy0tY29ubmVjdCcKICAgICAgICAtICctLWlubm9kYl9pbml0aWFsaXplZCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczo3LjQtYWxwaW5lJwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLXJlcXVpcmVwYXNzICR7U0VSVklDRV9QQVNTV09SRF9SRURJU30nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2ludm9pY2UtbmluamEtcmVkaXMtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtICctYScKICAgICAgICAtICcke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUK",
"tags": [
"invoicing",
"billing",
@@ -2362,7 +2376,7 @@
"plunk": {
"documentation": "https://docs.useplunk.com/getting-started/introduction?utm_source=coolify.io",
"slogan": "Plunk, The Open-Source Email Platform for AWS",
- "compose": "c2VydmljZXM6CiAgcGx1bms6CiAgICBpbWFnZTogJ2RyaWF1Zy9wbHVuazpsYXRlc3QnCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUExVTktfMzAwMAogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly9yZWRpczo2Mzc5JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXNxbDovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzcWwvcGx1bmstZGI/c2NoZW1hPXB1YmxpYycKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUU0VDUkVUfScKICAgICAgLSAnQVdTX1JFR0lPTj0ke0FXU19SRUdJT046P30nCiAgICAgIC0gJ0FXU19BQ0NFU1NfS0VZX0lEPSR7QVdTX0FDQ0VTU19LRVlfSUQ6P30nCiAgICAgIC0gJ0FXU19TRUNSRVRfQUNDRVNTX0tFWT0ke0FXU19TRUNSRVRfQUNDRVNTX0tFWTo/fScKICAgICAgLSAnQVdTX1NFU19DT05GSUdVUkFUSU9OX1NFVD0ke0FXU19TRVNfQ09ORklHVVJBVElPTl9TRVQ6P30nCiAgICAgIC0gJ05FWFRfUFVCTElDX0FQSV9VUkk9JHtTRVJWSUNFX0ZRRE5fUExVTkt9L2FwaScKICAgICAgLSAnQVBQX1VSST0ke1NFUlZJQ0VfRlFETl9QTFVOS30nCiAgICAgIC0gJ0FQSV9VUkk9JHtTRVJWSUNFX0ZRRE5fUExVTkt9L2FwaScKICAgICAgLSAnRElTQUJMRV9TSUdOVVBTPSR7RElTQUJMRV9TSUdOVVBTOi1GYWxzZX0nCiAgICBlbnRyeXBvaW50OgogICAgICAtIC9hcHAvZW50cnkuc2gKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1wbHVuay1kYn0nCiAgICB2b2x1bWVzOgogICAgICAtICdwbHVuay1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczo3LjQtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncGx1bmstcmVkaXMtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtIFBJTkcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAyMAo=",
+ "compose": "c2VydmljZXM6CiAgcGx1bms6CiAgICBpbWFnZTogJ2RyaWF1Zy9wbHVuazpsYXRlc3QnCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUExVTktfMzAwMAogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly9yZWRpczo2Mzc5JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXNxbDovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzcWwvcGx1bmstZGI/c2NoZW1hPXB1YmxpYycKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUU0VDUkVUfScKICAgICAgLSAnQVdTX1JFR0lPTj0ke0FXU19SRUdJT046P30nCiAgICAgIC0gJ0FXU19BQ0NFU1NfS0VZX0lEPSR7QVdTX0FDQ0VTU19LRVlfSUQ6P30nCiAgICAgIC0gJ0FXU19TRUNSRVRfQUNDRVNTX0tFWT0ke0FXU19TRUNSRVRfQUNDRVNTX0tFWTo/fScKICAgICAgLSAnQVdTX1NFU19DT05GSUdVUkFUSU9OX1NFVD0ke0FXU19TRVNfQ09ORklHVVJBVElPTl9TRVQ6P30nCiAgICAgIC0gJ05FWFRfUFVCTElDX0FQSV9VUkk9JHtTRVJWSUNFX0ZRRE5fUExVTkt9L2FwaScKICAgICAgLSAnQVBQX1VSST0ke1NFUlZJQ0VfRlFETl9QTFVOS30nCiAgICAgIC0gJ0FQSV9VUkk9JHtTRVJWSUNFX0ZRRE5fUExVTkt9L2FwaScKICAgICAgLSAnRElTQUJMRV9TSUdOVVBTPSR7RElTQUJMRV9TSUdOVVBTOi1GYWxzZX0nCiAgICAgIC0gTk9ERV9PUFRJT05TPS0tbm8tbmV0d29yay1mYW1pbHktYXV0b3NlbGVjdGlvbgogICAgZW50cnlwb2ludDoKICAgICAgLSAvYXBwL2VudHJ5LnNoCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJyh3Z2V0IC1TIC0tc3BpZGVyIGh0dHA6Ly8xMjcuMC4wLjE6MzAwMC9hcGkvaGVhbHRoIDI+JjEgfCBncmVwIC1xICJIVFRQLzEuMSBbMS0zXSIpJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1wbHVuay1kYn0nCiAgICB2b2x1bWVzOgogICAgICAtICdwbHVuay1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczo3LjQtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncGx1bmstcmVkaXMtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtIFBJTkcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAyMAo=",
"tags": [
"plunk",
"email",
diff --git a/versions.json b/versions.json
index a1a01432c..e27d8adc2 100644
--- a/versions.json
+++ b/versions.json
@@ -1,13 +1,13 @@
{
"coolify": {
"v4": {
- "version": "4.0.0-beta.383"
+ "version": "4.0.0-beta.389"
},
"nightly": {
- "version": "4.0.0-beta.384"
+ "version": "4.0.0-beta.390"
},
"helper": {
- "version": "1.0.4"
+ "version": "1.0.5"
},
"realtime": {
"version": "1.0.5"