Merge branch 'coollabsio:main' into service-getoutline

This commit is contained in:
peaklabs-dev
2024-09-12 13:05:27 +02:00
committed by GitHub
61 changed files with 2501 additions and 1628 deletions

View File

@@ -6,13 +6,7 @@ APP_KEY=
APP_URL=http://localhost APP_URL=http://localhost
APP_PORT=8000 APP_PORT=8000
APP_DEBUG=true APP_DEBUG=true
MUX_ENABLED=false SSH_MUX_ENABLED=false
# Enable Laravel Telescope for debugging
TELESCOPE_ENABLED=false
# Selenium Driver URL for Dusk
DUSK_DRIVER_URL=http://selenium:4444
# PostgreSQL Database Configuration # PostgreSQL Database Configuration
DB_DATABASE=coolify DB_DATABASE=coolify
@@ -27,6 +21,16 @@ RAY_ENABLED=false
# Set custom ray port # Set custom ray port
RAY_PORT= RAY_PORT=
# Clockwork Configuration
CLOCKWORK_ENABLED=false
CLOCKWORK_QUEUE_COLLECT=true
# Enable Laravel Telescope for debugging
TELESCOPE_ENABLED=false
# Selenium Driver URL for Dusk
DUSK_DRIVER_URL=http://selenium:4444
# Special Keys for Andras # Special Keys for Andras
# For cache purging # For cache purging
BUNNY_API_KEY= BUNNY_API_KEY=

View File

@@ -38,6 +38,8 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
labels: |
coolify.managed=true
aarch64: aarch64:
runs-on: [ self-hosted, arm64 ] runs-on: [ self-hosted, arm64 ]
permissions: permissions:
@@ -64,6 +66,8 @@ jobs:
platforms: linux/aarch64 platforms: linux/aarch64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
labels: |
coolify.managed=true
merge-manifest: merge-manifest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
@@ -94,3 +98,4 @@ jobs:
if: always() if: always()
with: with:
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}

View File

@@ -2,7 +2,7 @@ name: Coolify Helper Image (v4)
on: on:
push: push:
branches: [ "main" ] branches: [ "main", "next" ]
paths: paths:
- .github/workflows/coolify-helper.yml - .github/workflows/coolify-helper.yml
- docker/coolify-helper/Dockerfile - docker/coolify-helper/Dockerfile
@@ -38,6 +38,8 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
labels: |
coolify.managed=true
aarch64: aarch64:
runs-on: [ self-hosted, arm64 ] runs-on: [ self-hosted, arm64 ]
permissions: permissions:
@@ -64,6 +66,8 @@ jobs:
platforms: linux/aarch64 platforms: linux/aarch64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
labels: |
coolify.managed=true
merge-manifest: merge-manifest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:

View File

@@ -16,6 +16,11 @@ env:
jobs: jobs:
amd64: amd64:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Login to ghcr.io - name: Login to ghcr.io
@@ -37,6 +42,8 @@ jobs:
permissions: permissions:
contents: read contents: read
packages: write packages: write
attestations: write
id-token: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Login to ghcr.io - name: Login to ghcr.io
@@ -58,6 +65,8 @@ jobs:
permissions: permissions:
contents: read contents: read
packages: write packages: write
attestations: write
id-token: write
needs: [amd64, aarch64] needs: [amd64, aarch64]
steps: steps:
- name: Checkout - name: Checkout

View File

@@ -54,7 +54,7 @@ Special thanks to our biggest sponsors!
* [Latitude](https://latitude.sh/?ref=coolify.io) - A cloud computing platform offering bare metal servers and cloud instances for developers and businesses. * [Latitude](https://latitude.sh/?ref=coolify.io) - A cloud computing platform offering bare metal servers and cloud instances for developers and businesses.
* [Brand Dev](https://brand.dev/?ref=coolify.io) - A web development agency specializing in creating custom digital experiences and brand identities. * [Brand Dev](https://brand.dev/?ref=coolify.io) - A web development agency specializing in creating custom digital experiences and brand identities.
* [Jobscollider](https://jobscollider.com/remote-jobs?ref=coolify.io) - A job search platform connecting professionals with remote work opportunities across various industries. * [Jobscollider](https://jobscollider.com/remote-jobs?ref=coolify.io) - A job search platform connecting professionals with remote work opportunities across various industries.
* [Hostinger](https://hostinger.com?ref=coolify.io) - A web hosting provider offering affordable hosting solutions, domain registration, and website building tools. * [Hostinger](https://www.hostinger.com/vps/coolify-hosting?ref=coolify.io) - A web hosting provider offering affordable hosting solutions, domain registration, and website building tools.
* [Glueops](https://www.glueops.dev/?ref=coolify.io) - A DevOps consulting company providing infrastructure automation and cloud optimization services. * [Glueops](https://www.glueops.dev/?ref=coolify.io) - A DevOps consulting company providing infrastructure automation and cloud optimization services.
* [Ubicloud](https://ubicloud.com/?ref=coolify.io) - An open-source alternative to hyperscale cloud providers, offering high-performance cloud computing services. * [Ubicloud](https://ubicloud.com/?ref=coolify.io) - An open-source alternative to hyperscale cloud providers, offering high-performance cloud computing services.
* [Juxtdigital](https://juxtdigital.dev/?ref=coolify.io) - A digital agency offering web development, design, and digital marketing services for businesses. * [Juxtdigital](https://juxtdigital.dev/?ref=coolify.io) - A digital agency offering web development, design, and digital marketing services for businesses.

45
RELEASE.md Normal file
View File

@@ -0,0 +1,45 @@
# Coolify Release Guide
This guide outlines the release process for Coolify, intended for developers and those interested in understanding how releases are managed and deployed.
## Release Process
1. **Development on `next` or separate branches**
- Changes, fixes and new features are developed on the `next` or even separate branches.
2. **Merging to `main`**
- Once changes are ready, they are merged from `next` into the `main` branch.
3. **Building the release**
- After merging to `main`, a new release is built.
- Note: A push to `main` does not automatically mean a new version is released.
4. **Creating a GitHub release**
- A new release is created on GitHub with the new version details.
5. **Updating the CDN**
- The final step is updating the version information on the CDN:
[https://cdn.coollabs.io/coolify/versions.json](https://cdn.coollabs.io/coolify/versions.json)
> [!NOTE]
> The CDN update may not occur immediately after the GitHub release. It can happen hours or even days later due to additional testing, stability checks, or potential hotfixes.
## Version Availability
It's important to understand that a new version released on GitHub may not immediately become available for users to update (through manual or auto-update).
> [!IMPORTANT]
> If you see a new release on GitHub but haven't received the update, it's likely because the CDN hasn't been updated yet. This is intentional and ensures stability and allows for hotfixes before the new version is officially released.
## Manually Update to Specific Versions
> [!CAUTION]
> Updating to unreleased versions is not recommended and may cause issues. Use at your own risk!
To update your Coolify instance to a specific (unreleased) version, use the following command:
```bash
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash -s <version>
```
-> Replace `<version>` with the version you want to update to (for example `4.0.0-beta.332`).

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use App\Models\InstanceSettings;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
@@ -21,10 +22,16 @@ class CleanupDocker
private function getCommands(): array private function getCommands(): array
{ {
$settings = InstanceSettings::get();
$helperImageVersion = data_get($settings, 'helper_version');
$helperImage = config('coolify.helper_image');
$helperImageWithVersion = config('coolify.helper_image').':'.$helperImageVersion;
$commonCommands = [ $commonCommands = [
'docker container prune -f --filter "label=coolify.managed=true"', 'docker container prune -f --filter "label=coolify.managed=true"',
'docker image prune -af', 'docker image prune -af --filter "label!=coolify.managed=true"',
'docker builder prune -af', 'docker builder prune -af',
"docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi",
]; ];
return $commonCommands; return $commonCommands;

View File

@@ -16,8 +16,10 @@ class StartService
$service->saveComposeConfigs(); $service->saveComposeConfigs();
$commands[] = 'cd '.$service->workdir(); $commands[] = 'cd '.$service->workdir();
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'"; $commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
if($service->networks()->count() > 0){
$commands[] = "echo 'Creating Docker network.'"; $commands[] = "echo 'Creating Docker network.'";
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid"; $commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
}
$commands[] = 'echo Starting service.'; $commands[] = 'echo Starting service.';
$commands[] = "echo 'Pulling images.'"; $commands[] = "echo 'Pulling images.'";
$commands[] = 'docker compose pull'; $commands[] = 'docker compose pull';

View File

@@ -4,6 +4,7 @@ namespace App\Console;
use App\Jobs\CheckForUpdatesJob; use App\Jobs\CheckForUpdatesJob;
use App\Jobs\CleanupInstanceStuffsJob; use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\CleanupStaleMultiplexedConnections;
use App\Jobs\DatabaseBackupJob; use App\Jobs\DatabaseBackupJob;
use App\Jobs\DockerCleanupJob; use App\Jobs\DockerCleanupJob;
use App\Jobs\PullHelperImageJob; use App\Jobs\PullHelperImageJob;
@@ -29,7 +30,8 @@ class Kernel extends ConsoleKernel
$this->all_servers = Server::all(); $this->all_servers = Server::all();
$settings = InstanceSettings::get(); $settings = InstanceSettings::get();
$schedule->command('telescope:prune')->daily(); $schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
if (isDev()) { if (isDev()) {
// Instance Jobs // Instance Jobs
$schedule->command('horizon:snapshot')->everyMinute(); $schedule->command('horizon:snapshot')->everyMinute();
@@ -39,6 +41,8 @@ class Kernel extends ConsoleKernel
$this->check_resources($schedule); $this->check_resources($schedule);
$this->check_scheduled_tasks($schedule); $this->check_scheduled_tasks($schedule);
$schedule->command('uploads:clear')->everyTwoMinutes(); $schedule->command('uploads:clear')->everyTwoMinutes();
$schedule->command('telescope:prune')->daily();
} else { } else {
// Instance Jobs // Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes(); $schedule->command('horizon:snapshot')->everyFiveMinutes();

View File

@@ -86,7 +86,7 @@ class DeployController extends Controller
], ],
tags: ['Deployments'], tags: ['Deployments'],
parameters: [ parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment UUID', schema: new OA\Schema(type: 'string')),
], ],
responses: [ responses: [
new OA\Response( new OA\Response(
@@ -150,7 +150,7 @@ class DeployController extends Controller
responses: [ responses: [
new OA\Response( new OA\Response(
response: 200, response: 200,
description: 'Get deployment(s) Uuid\'s', description: 'Get deployment(s) UUID\'s',
content: [ content: [
new OA\MediaType( new OA\MediaType(
mediaType: 'application/json', mediaType: 'application/json',

View File

@@ -11,7 +11,7 @@ class ProjectController extends Controller
{ {
#[OA\Get( #[OA\Get(
summary: 'List', summary: 'List',
description: 'list projects.', description: 'List projects.',
path: '/projects', path: '/projects',
operationId: 'list-projects', operationId: 'list-projects',
security: [ security: [
@@ -47,7 +47,7 @@ class ProjectController extends Controller
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
$projects = Project::whereTeamId($teamId)->select('id', 'name', 'uuid')->get(); $projects = Project::whereTeamId($teamId)->select('id', 'name', 'description', 'uuid')->get();
return response()->json(serializeApiResponse($projects), return response()->json(serializeApiResponse($projects),
); );
@@ -55,7 +55,7 @@ class ProjectController extends Controller
#[OA\Get( #[OA\Get(
summary: 'Get', summary: 'Get',
description: 'Get project by Uuid.', description: 'Get project by UUID.',
path: '/projects/{uuid}', path: '/projects/{uuid}',
operationId: 'get-project-by-uuid', operationId: 'get-project-by-uuid',
security: [ security: [
@@ -139,7 +139,7 @@ class ProjectController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
if (! $request->uuid) { if (! $request->uuid) {
return response()->json(['message' => 'Uuid is required.'], 422); return response()->json(['message' => 'UUID is required.'], 422);
} }
if (! $request->environment_name) { if (! $request->environment_name) {
return response()->json(['message' => 'Environment name is required.'], 422); return response()->json(['message' => 'Environment name is required.'], 422);
@@ -341,7 +341,7 @@ class ProjectController extends Controller
} }
$uuid = $request->uuid; $uuid = $request->uuid;
if (! $uuid) { if (! $uuid) {
return response()->json(['message' => 'Uuid is required.'], 422); return response()->json(['message' => 'UUID is required.'], 422);
} }
$project = Project::whereTeamId($teamId)->whereUuid($uuid)->first(); $project = Project::whereTeamId($teamId)->whereUuid($uuid)->first();
@@ -417,7 +417,7 @@ class ProjectController extends Controller
} }
if (! $request->uuid) { if (! $request->uuid) {
return response()->json(['message' => 'Uuid is required.'], 422); return response()->json(['message' => 'UUID is required.'], 422);
} }
$project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first(); $project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
if (! $project) { if (! $project) {

View File

@@ -75,7 +75,7 @@ class SecurityController extends Controller
], ],
tags: ['Private Keys'], tags: ['Private Keys'],
parameters: [ parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key UUID', schema: new OA\Schema(type: 'string')),
], ],
responses: [ responses: [
new OA\Response( new OA\Response(
@@ -323,7 +323,7 @@ class SecurityController extends Controller
], ],
tags: ['Private Keys'], tags: ['Private Keys'],
parameters: [ parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key UUID', schema: new OA\Schema(type: 'string')),
], ],
responses: [ responses: [
new OA\Response( new OA\Response(

View File

@@ -107,7 +107,7 @@ class ServersController extends Controller
], ],
tags: ['Servers'], tags: ['Servers'],
parameters: [ parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s UUID', schema: new OA\Schema(type: 'string')),
], ],
responses: [ responses: [
new OA\Response( new OA\Response(
@@ -185,7 +185,7 @@ class ServersController extends Controller
], ],
tags: ['Servers'], tags: ['Servers'],
parameters: [ parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s UUID', schema: new OA\Schema(type: 'string')),
], ],
responses: [ responses: [
new OA\Response( new OA\Response(
@@ -263,7 +263,7 @@ class ServersController extends Controller
], ],
tags: ['Servers'], tags: ['Servers'],
parameters: [ parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s UUID', schema: new OA\Schema(type: 'string')),
], ],
responses: [ responses: [
new OA\Response( new OA\Response(

View File

@@ -378,7 +378,7 @@ class ServicesController extends Controller
responses: [ responses: [
new OA\Response( new OA\Response(
response: 200, response: 200,
description: 'Get a service by Uuid.', description: 'Get a service by UUID.',
content: [ content: [
new OA\MediaType( new OA\MediaType(
mediaType: 'application/json', mediaType: 'application/json',
@@ -436,7 +436,7 @@ class ServicesController extends Controller
responses: [ responses: [
new OA\Response( new OA\Response(
response: 200, response: 200,
description: 'Delete a service by Uuid', description: 'Delete a service by UUID',
content: [ content: [
new OA\MediaType( new OA\MediaType(
mediaType: 'application/json', mediaType: 'application/json',

View File

@@ -1066,15 +1066,55 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->environment_variables = $envs; $this->environment_variables = $envs;
} }
private function elixir_finetunes()
{
if ($this->pull_request_id === 0) {
$envType = 'environment_variables';
} else {
$envType = 'environment_variables_preview';
}
$mix_env = $this->application->{$envType}->where('key', 'MIX_ENV')->first();
if ($mix_env) {
if ($mix_env->is_build_time === false) {
$this->application_deployment_queue->addLogEntry('MIX_ENV environment variable is not set as build time.', type: 'error');
$this->application_deployment_queue->addLogEntry('Please set MIX_ENV environment variable to be build time variable if you facing any issues with the deployment.', type: 'error');
}
} else {
$this->application_deployment_queue->addLogEntry('MIX_ENV environment variable not found.', type: 'error');
$this->application_deployment_queue->addLogEntry('Please add MIX_ENV environment variable and set it to be build time variable if you facing any issues with the deployment.', type: 'error');
}
$secret_key_base = $this->application->{$envType}->where('key', 'SECRET_KEY_BASE')->first();
if ($secret_key_base) {
if ($secret_key_base->is_build_time === false) {
$this->application_deployment_queue->addLogEntry('SECRET_KEY_BASE environment variable is not set as build time.', type: 'error');
$this->application_deployment_queue->addLogEntry('Please set SECRET_KEY_BASE environment variable to be build time variable if you facing any issues with the deployment.', type: 'error');
}
} else {
$this->application_deployment_queue->addLogEntry('SECRET_KEY_BASE environment variable not found.', type: 'error');
$this->application_deployment_queue->addLogEntry('Please add SECRET_KEY_BASE environment variable and set it to be build time variable if you facing any issues with the deployment.', type: 'error');
}
$database_url = $this->application->{$envType}->where('key', 'DATABASE_URL')->first();
if ($database_url) {
if ($database_url->is_build_time === false) {
$this->application_deployment_queue->addLogEntry('DATABASE_URL environment variable is not set as build time.', type: 'error');
$this->application_deployment_queue->addLogEntry('Please set DATABASE_URL environment variable to be build time variable if you facing any issues with the deployment.', type: 'error');
}
} else {
$this->application_deployment_queue->addLogEntry('DATABASE_URL environment variable not found.', type: 'error');
$this->application_deployment_queue->addLogEntry('Please add DATABASE_URL environment variable and set it to be build time variable if you facing any issues with the deployment.', type: 'error');
}
}
private function laravel_finetunes() private function laravel_finetunes()
{ {
if ($this->pull_request_id === 0) { if ($this->pull_request_id === 0) {
$nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first(); $envType = 'environment_variables';
$nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
} else { } else {
$nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first(); $envType = 'environment_variables_preview';
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
} }
$nixpacks_php_fallback_path = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
if (! $nixpacks_php_fallback_path) { if (! $nixpacks_php_fallback_path) {
$nixpacks_php_fallback_path = new EnvironmentVariable; $nixpacks_php_fallback_path = new EnvironmentVariable;
$nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH'; $nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH';
@@ -1533,6 +1573,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
data_set($parsed, 'variables.NIXPACKS_PHP_FALLBACK_PATH', $variables[0]->value); data_set($parsed, 'variables.NIXPACKS_PHP_FALLBACK_PATH', $variables[0]->value);
data_set($parsed, 'variables.NIXPACKS_PHP_ROOT_DIR', $variables[1]->value); data_set($parsed, 'variables.NIXPACKS_PHP_ROOT_DIR', $variables[1]->value);
} }
if ($this->nixpacks_type === 'elixir') {
$this->elixir_finetunes();
}
$this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT); $this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT);
$this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true); $this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true);
if ($this->nixpacks_type === 'rust') { if ($this->nixpacks_type === 'rust') {

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Jobs;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Process;
class CleanupStaleMultiplexedConnections implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
Server::chunk(100, function ($servers) {
foreach ($servers as $server) {
$this->cleanupStaleConnection($server);
}
});
}
private function cleanupStaleConnection(Server $server)
{
$muxSocket = "/tmp/mux_{$server->id}";
$checkCommand = "ssh -O check -o ControlPath=$muxSocket {$server->user}@{$server->ip} 2>/dev/null";
$checkProcess = Process::run($checkCommand);
if ($checkProcess->exitCode() !== 0) {
$closeCommand = "ssh -O exit -o ControlPath=$muxSocket {$server->user}@{$server->ip} 2>/dev/null";
Process::run($closeCommand);
}
}
}

View File

@@ -25,7 +25,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use App\Models\InstanceSettings; use Visus\Cuid2\Cuid2;
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{ {
@@ -399,6 +399,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location"; $backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
$commands[] = $backupCommand; $commands[] = $backupCommand;
ray($commands);
$this->backup_output = instant_remote_process($commands, $this->server); $this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output); $this->backup_output = trim($this->backup_output);
if ($this->backup_output === '') { if ($this->backup_output === '') {
@@ -480,6 +481,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
private function upload_to_s3(): void private function upload_to_s3(): void
{ {
try { try {
ray($this->backup_location);
if (is_null($this->s3)) { if (is_null($this->s3)) {
return; return;
} }
@@ -489,62 +491,20 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$bucket = $this->s3->bucket; $bucket = $this->s3->bucket;
$endpoint = $this->s3->endpoint; $endpoint = $this->s3->endpoint;
$this->s3->testConnection(shouldSave: true); $this->s3->testConnection(shouldSave: true);
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') { $configName = new Cuid2;
$network = $this->database->service->destination->network;
} else {
$network = $this->database->destination->network;
}
$this->ensureHelperImageAvailable(); $s3_copy_dir = str($this->backup_location)->replace(backup_dir(), '/var/www/html/storage/app/backups/');
$commands[] = "docker exec coolify bash -c 'mc config host add {$configName} {$endpoint} $key $secret'";
$fullImageName = $this->getFullImageName(); $commands[] = "docker exec coolify bash -c 'mc cp $s3_copy_dir {$configName}/{$bucket}{$this->backup_dir}/'";
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server); instant_remote_process($commands, $this->server);
$this->add_to_backup_output('Uploaded to S3.'); $this->add_to_backup_output('Uploaded to S3.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage()); $this->add_to_backup_output($e->getMessage());
throw $e; throw $e;
} finally { } finally {
$command = "docker rm -f backup-of-{$this->backup->uuid}"; $removeConfigCommands[] = "docker exec coolify bash -c 'mc config remove {$configName}'";
instant_remote_process([$command], $this->server); $removeConfigCommands[] = "docker exec coolify bash -c 'mc alias rm {$configName}'";
instant_remote_process($removeConfigCommands, $this->server, false);
} }
} }
private function ensureHelperImageAvailable(): void
{
$fullImageName = $this->getFullImageName();
$imageExists = $this->checkImageExists($fullImageName);
if (!$imageExists) {
$this->pullHelperImage($fullImageName);
}
}
private function checkImageExists(string $fullImageName): bool
{
$result = instant_remote_process(["docker image inspect {$fullImageName} >/dev/null 2>&1 && echo 'exists' || echo 'not exists'"], $this->server, false);
return trim($result) === 'exists';
}
private function pullHelperImage(string $fullImageName): void
{
try {
instant_remote_process(["docker pull {$fullImageName}"], $this->server);
} catch (\Exception $e) {
$errorMessage = "Failed to pull helper image: " . $e->getMessage();
$this->add_to_backup_output($errorMessage);
throw new \RuntimeException($errorMessage);
}
}
private function getFullImageName(): string
{
$settings = InstanceSettings::get();
$helperImage = config('coolify.helper_image');
$latestVersion = $settings->helper_version;
return "{$helperImage}:{$latestVersion}";
}
} }

View File

@@ -26,6 +26,8 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
public $tries = 3; public $tries = 3;
public $timeout = 60;
public $containers; public $containers;
public $applications; public $applications;
@@ -43,15 +45,15 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(public Server $server) {} public function __construct(public Server $server) {}
// public function middleware(): array public function middleware(): array
// { {
// return [(new WithoutOverlapping($this->server->uuid))]; return [(new WithoutOverlapping($this->server->id))];
// } }
// public function uniqueId(): int public function uniqueId(): int
// { {
// return $this->server->uuid; return $this->server->id;
// } }
public function handle() public function handle()
{ {
@@ -124,7 +126,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
private function checkLogDrainContainer() private function checkLogDrainContainer()
{ {
if(! $this->server->isLogDrainEnabled()) { if (! $this->server->isLogDrainEnabled()) {
return; return;
} }
$foundLogDrainContainer = $this->containers->filter(function ($value, $key) { $foundLogDrainContainer = $this->containers->filter(function ($value, $key) {

View File

@@ -73,6 +73,8 @@ class Index extends Component
} }
$this->privateKeyName = generate_random_name(); $this->privateKeyName = generate_random_name();
$this->remoteServerName = generate_random_name(); $this->remoteServerName = generate_random_name();
$this->remoteServerPort = $this->remoteServerPort;
$this->remoteServerUser = $this->remoteServerUser;
if (isDev()) { if (isDev()) {
$this->privateKey = '-----BEGIN OPENSSH PRIVATE KEY----- $this->privateKey = '-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
@@ -154,6 +156,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->servers = Server::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); $this->servers = Server::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
if ($this->servers->count() > 0) { if ($this->servers->count() > 0) {
$this->selectedExistingServer = $this->servers->first()->id; $this->selectedExistingServer = $this->servers->first()->id;
$this->updateServerDetails();
$this->currentState = 'select-existing-server'; $this->currentState = 'select-existing-server';
return; return;
@@ -173,9 +176,18 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
} }
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id; $this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
$this->serverPublicKey = $this->createdServer->privateKey->publicKey(); $this->serverPublicKey = $this->createdServer->privateKey->publicKey();
$this->updateServerDetails();
$this->currentState = 'validate-server'; $this->currentState = 'validate-server';
} }
private function updateServerDetails()
{
if ($this->createdServer) {
$this->remoteServerPort = $this->createdServer->port;
$this->remoteServerUser = $this->createdServer->user;
}
}
public function getProxyType() public function getProxyType()
{ {
// Set Default Proxy Type // Set Default Proxy Type
@@ -235,11 +247,12 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
public function saveServer() public function saveServer()
{ {
$this->validate([ $this->validate([
'remoteServerName' => 'required', 'remoteServerName' => 'required|string',
'remoteServerHost' => 'required', 'remoteServerHost' => 'required|string',
'remoteServerPort' => 'required|integer', 'remoteServerPort' => 'required|integer',
'remoteServerUser' => 'required', 'remoteServerUser' => 'required|string',
]); ]);
$this->privateKey = formatPrivateKey($this->privateKey); $this->privateKey = formatPrivateKey($this->privateKey);
$foundServer = Server::whereIp($this->remoteServerHost)->first(); $foundServer = Server::whereIp($this->remoteServerHost)->first();
if ($foundServer) { if ($foundServer) {
@@ -269,7 +282,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
public function validateServer() public function validateServer()
{ {
try { try {
config()->set('coolify.mux_enabled', false); config()->set('constants.ssh.mux_enabled', false);
// EC2 does not have `uptime` command, lol // EC2 does not have `uptime` command, lol
instant_remote_process(['ls /'], $this->createdServer, true); instant_remote_process(['ls /'], $this->createdServer, true);
@@ -277,9 +290,12 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->createdServer->settings()->update([ $this->createdServer->settings()->update([
'is_reachable' => true, 'is_reachable' => true,
]); ]);
$this->serverReachable = true;
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->serverReachable = false; $this->serverReachable = false;
$this->createdServer->delete(); $this->createdServer->settings()->update([
'is_reachable' => false,
]);
return handleError(error: $e, livewire: $this); return handleError(error: $e, livewire: $this);
} }
@@ -296,6 +312,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
]); ]);
$this->getProxyType(); $this->getProxyType();
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->createdServer->settings()->update([
'is_usable' => false,
]);
return handleError(error: $e, livewire: $this); return handleError(error: $e, livewire: $this);
} }
} }
@@ -349,6 +369,21 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
); );
} }
public function saveAndValidateServer()
{
$this->validate([
'remoteServerPort' => 'required|integer|min:1|max:65535',
'remoteServerUser' => 'required|string',
]);
$this->createdServer->update([
'port' => $this->remoteServerPort,
'user' => $this->remoteServerUser,
'timezone' => 'UTC',
]);
$this->validateServer();
}
private function createNewPrivateKey() private function createNewPrivateKey()
{ {
$this->privateKeyName = generate_random_name(); $this->privateKeyName = generate_random_name();

View File

@@ -24,7 +24,11 @@ class Form extends Component
public $timezones; public $timezones;
protected $listeners = ['serverInstalled', 'revalidate' => '$refresh']; protected $listeners = [
'serverInstalled',
'refreshServerShow' => 'serverInstalled',
'revalidate' => '$refresh',
];
protected $rules = [ protected $rules = [
'server.name' => 'required', 'server.name' => 'required',

View File

@@ -2,10 +2,10 @@
namespace App\Livewire\Server\New; namespace App\Livewire\Server\New;
use App\Enums\ProxyStatus;
use App\Enums\ProxyTypes; use App\Enums\ProxyTypes;
use App\Models\Server; use App\Models\Server;
use App\Models\Team; use App\Models\Team;
use Illuminate\Support\Collection;
use Livewire\Component; use Livewire\Component;
class ByIp extends Component class ByIp extends Component
@@ -40,7 +40,7 @@ class ByIp extends Component
public bool $is_build_server = false; public bool $is_build_server = false;
public $swarm_managers = []; public Collection $swarm_managers;
protected $rules = [ protected $rules = [
'name' => 'required|string', 'name' => 'required|string',
@@ -102,11 +102,6 @@ class ByIp extends Component
'port' => $this->port, 'port' => $this->port,
'team_id' => currentTeam()->id, 'team_id' => currentTeam()->id,
'private_key_id' => $this->private_key_id, 'private_key_id' => $this->private_key_id,
'proxy' => [
// set default proxy type to traefik v2
'type' => ProxyTypes::TRAEFIK->value,
'status' => ProxyStatus::EXITED->value,
],
]; ];
if ($this->is_swarm_worker) { if ($this->is_swarm_worker) {
$payload['swarm_cluster'] = $this->selected_swarm_cluster; $payload['swarm_cluster'] = $this->selected_swarm_cluster;
@@ -115,6 +110,9 @@ class ByIp extends Component
data_forget($payload, 'proxy'); data_forget($payload, 'proxy');
} }
$server = Server::create($payload); $server = Server::create($payload);
$server->proxy->set('status', 'exited');
$server->proxy->set('type', ProxyTypes::TRAEFIK->value);
$server->save();
if ($this->is_build_server) { if ($this->is_build_server) {
$this->is_swarm_manager = false; $this->is_swarm_manager = false;
$this->is_swarm_worker = false; $this->is_swarm_worker = false;

View File

@@ -14,7 +14,7 @@ class Show extends Component
public $parameters = []; public $parameters = [];
protected $listeners = ['refreshServerShow' => '$refresh']; protected $listeners = ['refreshServerShow'];
public function mount() public function mount()
{ {
@@ -29,6 +29,12 @@ class Show extends Component
} }
} }
public function refreshServerShow()
{
$this->server->refresh();
$this->dispatch('$refresh');
}
public function submit() public function submit()
{ {
$this->dispatch('serverRefresh', false); $this->dispatch('serverRefresh', false);

View File

@@ -11,6 +11,7 @@ use OpenApi\Attributes as OA;
'id' => ['type' => 'integer'], 'id' => ['type' => 'integer'],
'uuid' => ['type' => 'string'], 'uuid' => ['type' => 'string'],
'name' => ['type' => 'string'], 'name' => ['type' => 'string'],
'description' => ['type' => 'string'],
'environments' => new OA\Property( 'environments' => new OA\Property(
property: 'environments', property: 'environments',
type: 'array', type: 'array',

View File

@@ -112,6 +112,16 @@ class Server extends BaseModel
'proxy', 'proxy',
]; ];
protected $fillable = [
'name',
'ip',
'port',
'user',
'description',
'private_key_id',
'team_id',
];
protected $guarded = []; protected $guarded = [];
public static function isReachable() public static function isReachable()
@@ -957,7 +967,7 @@ $schema://$host {
public function validateConnection() public function validateConnection()
{ {
config()->set('coolify.mux_enabled', false); config()->set('constants.ssh.mux_enabled', false);
$server = Server::find($this->id); $server = Server::find($this->id);
if (! $server) { if (! $server) {

View File

@@ -667,7 +667,7 @@ class Service extends BaseModel
} }
$data = $data->merge([ $data = $data->merge([
'Root User' => [ 'Root User' => [
'key' => 'N/A', 'key' => 'GITLAB_ROOT_USER',
'value' => 'root', 'value' => 'root',
'rules' => 'required', 'rules' => 'required',
'isPassword' => true, 'isPassword' => true,
@@ -1016,10 +1016,20 @@ class Service extends BaseModel
$commands[] = 'rm -f .env || true'; $commands[] = 'rm -f .env || true';
$envs_from_coolify = $this->environment_variables()->get(); $envs_from_coolify = $this->environment_variables()->get();
foreach ($envs_from_coolify as $env) { $sorted = $envs_from_coolify->sortBy(function ($env) {
if (str($env->key)->startsWith('SERVICE_')) {
return 1;
}
if (str($env->value)->startsWith('$SERVICE_') || str($env->value)->startsWith('${SERVICE_')) {
return 2;
}
return 3;
});
foreach ($sorted as $env) {
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env"; $commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
} }
if ($envs_from_coolify->count() === 0) { if ($sorted->count() === 0) {
$commands[] = 'touch .env'; $commands[] = 'touch .env';
} }
instant_remote_process($commands, $this->server); instant_remote_process($commands, $this->server);

View File

@@ -209,7 +209,7 @@ class StandaloneKeydb extends BaseModel
protected function internalDbUrl(): Attribute protected function internalDbUrl(): Attribute
{ {
return new Attribute( return new Attribute(
get: fn () => "redis://{$this->keydb_password}@{$this->uuid}:6379/0", get: fn () => "redis://:{$this->keydb_password}@{$this->uuid}:6379/0",
); );
} }
@@ -218,7 +218,7 @@ class StandaloneKeydb extends BaseModel
return new Attribute( return new Attribute(
get: function () { get: function () {
if ($this->is_public && $this->public_port) { if ($this->is_public && $this->public_port) {
return "redis://{$this->keydb_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; return "redis://:{$this->keydb_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
} }
return null; return null;

View File

@@ -46,6 +46,7 @@ const SUPPORTED_OS = [
'centos fedora rhel ol rocky amzn almalinux', 'centos fedora rhel ol rocky amzn almalinux',
'sles opensuse-leap opensuse-tumbleweed', 'sles opensuse-leap opensuse-tumbleweed',
'arch', 'arch',
'alpine',
]; ];
const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment']; const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment'];

View File

@@ -95,8 +95,24 @@ function generateScpCommand(Server $server, string $source, string $dest)
$timeout = config('constants.ssh.command_timeout'); $timeout = config('constants.ssh.command_timeout');
$connectionTimeout = config('constants.ssh.connection_timeout'); $connectionTimeout = config('constants.ssh.connection_timeout');
$serverInterval = config('constants.ssh.server_interval'); $serverInterval = config('constants.ssh.server_interval');
$muxPersistTime = config('constants.ssh.mux_persist_time');
$scp_command = "timeout $timeout scp "; $scp_command = "timeout $timeout scp ";
$muxEnabled = config('constants.ssh.mux_enabled', true) && config('coolify.is_windows_docker_desktop') == false;
// ray('SSH Multiplexing Enabled:', $muxEnabled)->blue();
if ($muxEnabled) {
$muxSocket = "/var/www/html/storage/app/ssh/mux/{$server->muxFilename()}";
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
ensureMultiplexedConnection($server);
// ray('Using SSH Multiplexing')->green();
} else {
// ray('Not using SSH Multiplexing')->red();
}
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$scp_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
}
$scp_command .= "-i {$privateKeyLocation} " $scp_command .= "-i {$privateKeyLocation} "
.'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ' .'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
.'-o PasswordAuthentication=no ' .'-o PasswordAuthentication=no '
@@ -145,9 +161,18 @@ function generateSshCommand(Server $server, string $command)
$ssh_command = "timeout $timeout ssh "; $ssh_command = "timeout $timeout ssh ";
if (config('coolify.mux_enabled') && config('coolify.is_windows_docker_desktop') == false) { $muxEnabled = config('constants.ssh.mux_enabled') && config('coolify.is_windows_docker_desktop') == false;
$ssh_command .= "-o ControlMaster=auto -o ControlPersist={$muxPersistTime} -o ControlPath=/var/www/html/storage/app/ssh/mux/{$server->muxFilename()} "; // ray('SSH Multiplexing Enabled:', $muxEnabled)->blue();
if ($muxEnabled) {
// Always use multiplexing when enabled
$muxSocket = "/var/www/html/storage/app/ssh/mux/{$server->muxFilename()}";
$ssh_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
ensureMultiplexedConnection($server);
// ray('Using SSH Multiplexing')->green();
} else {
// ray('Not using SSH Multiplexing')->red();
} }
if (data_get($server, 'settings.is_cloudflare_tunnel')) { if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" '; $ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
} }
@@ -169,8 +194,118 @@ function generateSshCommand(Server $server, string $command)
return $ssh_command; return $ssh_command;
} }
function ensureMultiplexedConnection(Server $server)
{
if (! (config('constants.ssh.mux_enabled') && config('coolify.is_windows_docker_desktop') == false)) {
return;
}
static $ensuredConnections = [];
if (isset($ensuredConnections[$server->id])) {
if (! shouldResetMultiplexedConnection($server)) {
// ray('Using Existing Multiplexed Connection')->green();
return;
}
}
$muxSocket = "/var/www/html/storage/app/ssh/mux/{$server->muxFilename()}";
$checkCommand = "ssh -O check -o ControlPath=$muxSocket ";
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$checkCommand .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
}
$checkCommand .= " {$server->user}@{$server->ip}";
$process = Process::run($checkCommand);
if ($process->exitCode() === 0) {
// ray('Existing Multiplexed Connection is Valid')->green();
$ensuredConnections[$server->id] = [
'timestamp' => now(),
'muxSocket' => $muxSocket,
];
return;
}
// ray('Establishing New Multiplexed Connection')->orange();
$privateKeyLocation = savePrivateKeyToFs($server);
$connectionTimeout = config('constants.ssh.connection_timeout');
$serverInterval = config('constants.ssh.server_interval');
$muxPersistTime = config('constants.ssh.mux_persist_time');
$establishCommand = "ssh -fNM -o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$establishCommand .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
}
$establishCommand .= "-i {$privateKeyLocation} "
.'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
.'-o PasswordAuthentication=no '
."-o ConnectTimeout=$connectionTimeout "
."-o ServerAliveInterval=$serverInterval "
.'-o RequestTTY=no '
.'-o LogLevel=ERROR '
."-p {$server->port} "
."{$server->user}@{$server->ip}";
$establishProcess = Process::run($establishCommand);
if ($establishProcess->exitCode() !== 0) {
throw new \RuntimeException('Failed to establish multiplexed connection: '.$establishProcess->errorOutput());
}
$ensuredConnections[$server->id] = [
'timestamp' => now(),
'muxSocket' => $muxSocket,
];
// ray('Established New Multiplexed Connection')->green();
}
function shouldResetMultiplexedConnection(Server $server)
{
if (! (config('constants.ssh.mux_enabled') && config('coolify.is_windows_docker_desktop') == false)) {
return false;
}
static $ensuredConnections = [];
if (! isset($ensuredConnections[$server->id])) {
return true;
}
$lastEnsured = $ensuredConnections[$server->id]['timestamp'];
$muxPersistTime = config('constants.ssh.mux_persist_time');
$resetInterval = strtotime($muxPersistTime) - time();
return $lastEnsured->addSeconds($resetInterval)->isPast();
}
function resetMultiplexedConnection(Server $server)
{
if (! (config('constants.ssh.mux_enabled') && config('coolify.is_windows_docker_desktop') == false)) {
return;
}
static $ensuredConnections = [];
if (isset($ensuredConnections[$server->id])) {
$muxSocket = $ensuredConnections[$server->id]['muxSocket'];
$closeCommand = "ssh -O exit -o ControlPath=$muxSocket {$server->user}@{$server->ip}";
Process::run($closeCommand);
unset($ensuredConnections[$server->id]);
}
}
function instant_remote_process(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false): ?string function instant_remote_process(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false): ?string
{ {
static $processCount = 0;
$processCount++;
$timeout = config('constants.ssh.command_timeout'); $timeout = config('constants.ssh.command_timeout');
if ($command instanceof Collection) { if ($command instanceof Collection) {
$command = $command->toArray(); $command = $command->toArray();
@@ -179,10 +314,18 @@ function instant_remote_process(Collection|array $command, Server $server, bool
$command = parseCommandsByLineForSudo(collect($command), $server); $command = parseCommandsByLineForSudo(collect($command), $server);
} }
$command_string = implode("\n", $command); $command_string = implode("\n", $command);
$ssh_command = generateSshCommand($server, $command_string, $no_sudo);
$process = Process::timeout($timeout)->run($ssh_command); $start_time = microtime(true);
$sshCommand = generateSshCommand($server, $command_string);
$process = Process::timeout($timeout)->run($sshCommand);
$end_time = microtime(true);
$execution_time = ($end_time - $start_time) * 1000; // Convert to milliseconds
// ray('SSH command execution time:', $execution_time.' ms')->orange();
$output = trim($process->output()); $output = trim($process->output());
$exitCode = $process->exitCode(); $exitCode = $process->exitCode();
if ($exitCode !== 0) { if ($exitCode !== 0) {
if (! $throwError) { if (! $throwError) {
return null; return null;
@@ -222,7 +365,6 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
if (is_null($application_deployment_queue)) { if (is_null($application_deployment_queue)) {
return collect([]); return collect([]);
} }
// ray(data_get($application_deployment_queue, 'logs'));
try { try {
$decoded = json_decode( $decoded = json_decode(
data_get($application_deployment_queue, 'logs'), data_get($application_deployment_queue, 'logs'),
@@ -232,7 +374,6 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
} catch (\JsonException $exception) { } catch (\JsonException $exception) {
return collect([]); return collect([]);
} }
// ray($decoded );
$seenCommands = collect(); $seenCommands = collect();
$formatted = collect($decoded); $formatted = collect($decoded);
if (! $is_debug_enabled) { if (! $is_debug_enabled) {
@@ -246,35 +387,35 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
return $i; return $i;
}) })
->reduce(function ($deploymentLogLines, $logItem) use ($seenCommands) { ->reduce(function ($deploymentLogLines, $logItem) use ($seenCommands) {
$command = $logItem['command']; $command = data_get($logItem, 'command');
$isStderr = $logItem['type'] === 'stderr'; $isStderr = data_get($logItem, 'type') === 'stderr';
$isNewCommand = ! is_null($command) && ! $seenCommands->first(function ($seenCommand) use ($logItem) { $isNewCommand = ! is_null($command) && ! $seenCommands->first(function ($seenCommand) use ($logItem) {
return $seenCommand['command'] === $logItem['command'] && $seenCommand['batch'] === $logItem['batch']; return data_get($seenCommand, 'command') === data_get($logItem, 'command') && data_get($seenCommand, 'batch') === data_get($logItem, 'batch');
}); });
if ($isNewCommand) { if ($isNewCommand) {
$deploymentLogLines->push([ $deploymentLogLines->push([
'line' => $command, 'line' => $command,
'timestamp' => $logItem['timestamp'], 'timestamp' => data_get($logItem, 'timestamp'),
'stderr' => $isStderr, 'stderr' => $isStderr,
'hidden' => $logItem['hidden'], 'hidden' => data_get($logItem, 'hidden'),
'command' => true, 'command' => true,
]); ]);
$seenCommands->push([ $seenCommands->push([
'command' => $command, 'command' => $command,
'batch' => $logItem['batch'], 'batch' => data_get($logItem, 'batch'),
]); ]);
} }
$lines = explode(PHP_EOL, $logItem['output']); $lines = explode(PHP_EOL, data_get($logItem, 'output'));
foreach ($lines as $line) { foreach ($lines as $line) {
$deploymentLogLines->push([ $deploymentLogLines->push([
'line' => $line, 'line' => $line,
'timestamp' => $logItem['timestamp'], 'timestamp' => data_get($logItem, 'timestamp'),
'stderr' => $isStderr, 'stderr' => $isStderr,
'hidden' => $logItem['hidden'], 'hidden' => data_get($logItem, 'hidden'),
]); ]);
} }
@@ -293,6 +434,10 @@ function remove_mux_and_private_key(Server $server)
{ {
$muxFilename = $server->muxFilename(); $muxFilename = $server->muxFilename();
$privateKeyLocation = savePrivateKeyToFs($server); $privateKeyLocation = savePrivateKeyToFs($server);
$closeCommand = "ssh -O exit -o ControlPath=/var/www/html/storage/app/ssh/mux/{$muxFilename} {$server->user}@{$server->ip}";
Process::run($closeCommand);
Storage::disk('ssh-mux')->delete($muxFilename); Storage::disk('ssh-mux')->delete($muxFilename);
Storage::disk('ssh-keys')->delete($privateKeyLocation); Storage::disk('ssh-keys')->delete($privateKeyLocation);
} }
@@ -302,7 +447,10 @@ function refresh_server_connection(?PrivateKey $private_key = null)
return; return;
} }
foreach ($private_key->servers as $server) { foreach ($private_key->servers as $server) {
Storage::disk('ssh-mux')->delete($server->muxFilename()); $muxFilename = $server->muxFilename();
$closeCommand = "ssh -O exit -o ControlPath=/var/www/html/storage/app/ssh/mux/{$muxFilename} {$server->user}@{$server->ip}";
Process::run($closeCommand);
Storage::disk('ssh-mux')->delete($muxFilename);
} }
} }
@@ -312,24 +460,17 @@ function checkRequiredCommands(Server $server)
foreach ($commands as $command) { foreach ($commands as $command) {
$commandFound = instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'command -v {$command}'"], $server, false); $commandFound = instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'command -v {$command}'"], $server, false);
if ($commandFound) { if ($commandFound) {
ray($command.' found');
continue; continue;
} }
try { try {
instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'apt update && apt install -y {$command}'"], $server); instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'apt update && apt install -y {$command}'"], $server);
} catch (\Throwable $e) { } catch (\Throwable $e) {
ray('could not install '.$command);
ray($e);
break; break;
} }
$commandFound = instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'command -v {$command}'"], $server, false); $commandFound = instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'command -v {$command}'"], $server, false);
if ($commandFound) { if ($commandFound) {
ray($command.' found');
continue; continue;
} }
ray('could not install '.$command);
break; break;
} }
} }

View File

@@ -4,6 +4,7 @@ use App\Models\Application;
use App\Models\EnvironmentVariable; use App\Models\EnvironmentVariable;
use App\Models\ServiceApplication; use App\Models\ServiceApplication;
use App\Models\ServiceDatabase; use App\Models\ServiceDatabase;
use Illuminate\Support\Stringable;
use Spatie\Url\Url; use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
@@ -15,9 +16,9 @@ function collectRegex(string $name)
{ {
return "/{$name}\w+/"; return "/{$name}\w+/";
} }
function replaceVariables($variable) function replaceVariables(string $variable): Stringable
{ {
return $variable->before('}')->replaceFirst('$', '')->replaceFirst('{', ''); return str($variable)->before('}')->replaceFirst('$', '')->replaceFirst('{', '');
} }
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Application $oneService, bool $isInit = false) function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Application $oneService, bool $isInit = false)

View File

@@ -1866,7 +1866,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key, 'key' => $key,
'service_id' => $resource->id, 'service_id' => $resource->id,
])->first(); ])->first();
$value = str(replaceVariables($value)); $value = replaceVariables($value);
$key = $value; $key = $value;
if ($value->startsWith('SERVICE_')) { if ($value->startsWith('SERVICE_')) {
$foundEnv = EnvironmentVariable::where([ $foundEnv = EnvironmentVariable::where([
@@ -2627,7 +2627,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'application_id' => $resource->id, 'application_id' => $resource->id,
'is_preview' => false, 'is_preview' => false,
])->first(); ])->first();
$value = str(replaceVariables($value)); $value = replaceVariables($value);
$key = $value; $key = $value;
if ($value->startsWith('SERVICE_')) { if ($value->startsWith('SERVICE_')) {
$foundEnv = EnvironmentVariable::where([ $foundEnv = EnvironmentVariable::where([
@@ -2864,6 +2864,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return collect($finalServices); return collect($finalServices);
} }
} }
function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection
{ {
$isApplication = $resource instanceof Application; $isApplication = $resource instanceof Application;
@@ -2920,6 +2921,182 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} }
$parsedServices = collect([]); $parsedServices = collect([]);
ray()->clearAll();
$allMagicEnvironments = collect([]);
foreach ($services as $serviceName => $service) {
$magicEnvironments = collect([]);
$image = data_get_str($service, 'image');
$environment = collect(data_get($service, 'environment', []));
$buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs);
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
if ($isService) {
if ($isDatabase) {
$savedService = ServiceDatabase::firstOrCreate([
'name' => $serviceName,
'image' => $image,
'service_id' => $resource->id,
]);
} else {
$savedService = ServiceApplication::firstOrCreate([
'name' => $serviceName,
'image' => $image,
'service_id' => $resource->id,
]);
}
$environment = collect(data_get($service, 'environment', []));
$buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs);
// convert environment variables to one format
$environment = convertComposeEnvironmentToArray($environment);
// Add Coolify defined environments
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
return [$item['key'] => $item['value']];
});
// filter and add magic environments
foreach ($environment as $key => $value) {
// Get all SERVICE_ variables from keys and values
$key = str($key);
$value = str($value);
$regex = '/\$(\{?([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\}?)/';
preg_match_all($regex, $value, $valueMatches);
if (count($valueMatches[1]) > 0) {
foreach ($valueMatches[1] as $match) {
$match = replaceVariables($match);
if ($match->startsWith('SERVICE_')) {
if ($magicEnvironments->has($match->value())) {
continue;
}
$magicEnvironments->put($match->value(), '');
}
}
}
// Get magic environments where we need to preset the FQDN
if ($key->startsWith('SERVICE_FQDN_')) {
// SERVICE_FQDN_APP or SERVICE_FQDN_APP_3000
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
if ($fqdnFor) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
} else {
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
}
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
if ($path !== '/') {
$fqdn = "$fqdn$path";
}
}
if ($isApplication && is_null($resource->fqdn)) {
data_forget($resource, 'environment_variables');
data_forget($resource, 'environment_variables_preview');
$resource->fqdn = $fqdn;
$resource->save();
} elseif ($isService && is_null($savedService->fqdn)) {
$savedService->fqdn = $fqdn;
$savedService->save();
}
if (substr_count(str($key)->value(), '_') === 2) {
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key->value(),
$nameOfId => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
'is_preview' => false,
]);
}
if (substr_count(str($key)->value(), '_') === 3) {
$newKey = str($key)->beforeLast('_');
$resource->environment_variables()->where('key', $newKey->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $newKey->value(),
$nameOfId => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
'is_preview' => false,
]);
}
}
}
$allMagicEnvironments = $allMagicEnvironments->merge($magicEnvironments);
if ($magicEnvironments->count() > 0) {
foreach ($magicEnvironments as $key => $value) {
$key = str($key);
$value = replaceVariables($value);
$command = $key->after('SERVICE_')->before('_');
$found = $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->first();
if ($found) {
continue;
}
if ($command->value() === 'FQDN') {
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
}
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key->value(),
$nameOfId => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
'is_preview' => false,
]);
} elseif ($command->value() === 'URL') {
$fqdnFor = $key->after('SERVICE_URL_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
}
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key->value(),
$nameOfId => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
'is_preview' => false,
]);
} else {
$value = generateEnvValue($command, $resource);
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key->value(),
$nameOfId => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
'is_preview' => false,
]);
}
}
}
}
}
// Parse the rest of the services
foreach ($services as $serviceName => $service) { foreach ($services as $serviceName => $service) {
$image = data_get_str($service, 'image'); $image = data_get_str($service, 'image');
$restart = data_get_str($service, 'restart', RESTART_MODE); $restart = data_get_str($service, 'restart', RESTART_MODE);
@@ -2932,12 +3109,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} }
$volumes = collect(data_get($service, 'volumes', [])); $volumes = collect(data_get($service, 'volumes', []));
$networks = collect(data_get($service, 'networks', [])); $networks = collect(data_get($service, 'networks', []));
$use_network_mode = data_get($service, 'network_mode') !== null;
$depends_on = collect(data_get($service, 'depends_on', [])); $depends_on = collect(data_get($service, 'depends_on', []));
$labels = collect(data_get($service, 'labels', [])); $labels = collect(data_get($service, 'labels', []));
$environment = collect(data_get($service, 'environment', [])); $environment = collect(data_get($service, 'environment', []));
$ports = collect(data_get($service, 'ports', [])); $ports = collect(data_get($service, 'ports', []));
$buildArgs = collect(data_get($service, 'build.args', [])); $buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs); $environment = $environment->merge($buildArgs);
$environment = convertComposeEnvironmentToArray($environment);
$coolifyEnvironments = collect([]);
$isDatabase = isDatabaseImage(data_get_str($service, 'image')); $isDatabase = isDatabaseImage(data_get_str($service, 'image'));
$volumesParsed = collect([]); $volumesParsed = collect([]);
@@ -3069,10 +3251,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($topLevel->get('volumes')->has($source->value())) { if ($topLevel->get('volumes')->has($source->value())) {
$temp = $topLevel->get('volumes')->get($source->value()); $temp = $topLevel->get('volumes')->get($source->value());
if (data_get($temp, 'driver_opts.type') === 'cifs') { if (data_get($temp, 'driver_opts.type') === 'cifs') {
return $volume; continue;
} }
if (data_get($temp, 'driver_opts.type') === 'nfs') { if (data_get($temp, 'driver_opts.type') === 'nfs') {
return $volume; continue;
} }
} }
$slugWithoutUuid = Str::slug($source, '-'); $slugWithoutUuid = Str::slug($source, '-');
@@ -3127,6 +3309,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$depends_on = $newDependsOn; $depends_on = $newDependsOn;
} }
} }
if (! $use_network_mode) {
if ($topLevel->get('networks')?->count() > 0) { if ($topLevel->get('networks')?->count() > 0) {
foreach ($topLevel->get('networks') as $networkName => $network) { foreach ($topLevel->get('networks') as $networkName => $network) {
if ($networkName === 'default') { if ($networkName === 'default') {
@@ -3155,6 +3338,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
]); ]);
} }
} }
}
// Collect/create/update ports // Collect/create/update ports
$collectedPorts = collect([]); $collectedPorts = collect([]);
@@ -3178,6 +3362,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$networks_temp = collect(); $networks_temp = collect();
if (! $use_network_mode) {
foreach ($networks as $key => $network) { foreach ($networks as $key => $network) {
if (gettype($network) === 'string') { if (gettype($network) === 'string') {
// networks: // networks:
@@ -3204,176 +3389,67 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
]); ]);
} }
} }
// convert environment variables to one format }
$environment = convertComposeEnvironmentToArray($environment);
// Add Coolify defined environments $normalEnvironments = $environment->diffKeys($allMagicEnvironments);
$allEnvironments = $resource->environment_variables()->get(['key', 'value']); $normalEnvironments = $normalEnvironments->filter(function ($value, $key) {
return ! str($value)->startsWith('SERVICE_');
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
return [$item['key'] => $item['value']];
}); });
// remove $environment from $allEnvironments foreach ($normalEnvironments as $key => $value) {
$coolifyDefinedEnvironments = $allEnvironments->diffKeys($environment);
// filter magic environments
$magicEnvironments = $environment->filter(function ($value, $key) {
$regex = '/\$\{(.*?)\}/';
preg_match_all($regex, $value, $matches);
if (count($matches[1]) > 0) {
foreach ($matches[1] as $match) {
if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
return $match;
}
}
}
$value = str(replaceVariables(str($value)));
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
});
foreach ($environment as $key => $value) {
$regex = '/\$\{(.*?)\}/';
preg_match_all($regex, $value, $matches);
if (count($matches[1]) > 0) {
foreach ($matches[1] as $match) {
if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
$magicEnvironments->put($match, '$'.$match);
}
}
$magicEnvironments->forget($key);
}
}
$normalEnvironments = $environment->diffKeys($magicEnvironments);
if ($magicEnvironments->count() > 0) {
foreach ($magicEnvironments as $key => $value) {
$key = str($key); $key = str($key);
$value = str(replaceVariables(str($value))); $value = str($value);
$originalValue = $value; $originalValue = $value;
$keyCommand = $key->after('SERVICE_')->before('_'); $parsedValue = replaceVariables($value);
$valueCommand = $value->after('SERVICE_')->before('_'); if ($value->startsWith('$SERVICE_')) {
if ($key->startsWith('SERVICE_FQDN_')) { $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value(); 'key' => $key,
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} elseif ($value->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $value->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} else {
$fqdnFor = null;
}
if ($keyCommand->value() === 'FQDN' || $valueCommand->value() === 'FQDN') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
if ($fqdnFor) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
} else {
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
}
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
if ($value === '/') {
$value = "$fqdn";
} else {
$value = "$fqdn$path";
}
} else {
$value = $fqdn;
}
if (! $isDatabase) {
if ($key->startsWith('SERVICE_FQDN_') && ($originalValue->value() === '' || $originalValue->startsWith('/'))) {
if ($isApplication && is_null($resource->fqdn)) {
data_forget($resource, 'environment_variables');
data_forget($resource, 'environment_variables_preview');
$resource->fqdn = $value;
$resource->save();
} elseif ($isService && is_null($savedService->fqdn)) {
if ($key->startsWith('SERVICE_FQDN_')) {
$savedService->fqdn = $value;
$savedService->save();
}
}
}
}
} elseif ($keyCommand->value() === 'URL' || $valueCommand->value() === 'URL') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
} elseif ($isService) {
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
$value = "$fqdn$path";
} else {
$value = $fqdn;
}
$value = str($fqdn)->replace('http://', '')->replace('https://', '');
} else {
$generatedValue = generateEnvValue($valueCommand, $resource);
if ($generatedValue) {
$value = $generatedValue;
}
}
if (str($fqdnFor)->startsWith('/')) {
$fqdnFor = null;
}
// Lets save the magic value to the environment variables
if (! $originalValue->startsWith('/')) {
if ($key->startsWith('SERVICE_')) {
$originalValue = $key;
}
$resource->environment_variables()->where('key', $originalValue->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $originalValue->value(),
$nameOfId => $resource->id, $nameOfId => $resource->id,
], [ ], [
'value' => $value, 'value' => $value,
'is_build_time' => false, 'is_build_time' => false,
'is_preview' => false, 'is_preview' => false,
]); ]);
continue;
} }
// Save the original value to the environment variables if (! $value->startsWith('$')) {
if ($originalValue->startsWith('SERVICE_')) { continue;
$value = "$$originalValue";
} }
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([ if ($key->value() === $parsedValue->value()) {
'key' => $key->value(), $value = null;
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key,
$nameOfId => $resource->id, $nameOfId => $resource->id,
], [ ], [
'value' => "$value", 'value' => $value,
'is_build_time' => false, 'is_build_time' => false,
'is_preview' => false, 'is_preview' => false,
]); ]);
} } else {
} if ($value->startsWith('$')) {
foreach ($normalEnvironments as $key => $value) {
$key = str($key);
$value = str($value);
if ($value->startsWith('$') || $value->contains('${')) {
if ($value->contains('${')) {
$value = $value->after('${')->before('}');
}
$value = str(replaceVariables(str($value)));
if ($value->contains(':-')) { if ($value->contains(':-')) {
$value = replaceVariables($value);
$key = $value->before(':'); $key = $value->before(':');
$value = $value->after(':-'); $value = $value->after(':-');
} elseif ($value->contains('-')) { } elseif ($value->contains('-')) {
$value = replaceVariables($value);
$key = $value->before('-'); $key = $value->before('-');
$value = $value->after('-'); $value = $value->after('-');
} elseif ($value->contains(':?')) { } elseif ($value->contains(':?')) {
$value = replaceVariables($value);
$key = $value->before(':'); $key = $value->before(':');
$value = $value->after(':?'); $value = $value->after(':?');
} elseif ($value->contains('?')) { } elseif ($value->contains('?')) {
$value = replaceVariables($value);
$key = $value->before('?'); $key = $value->before('?');
$value = $value->after('?'); $value = $value->after('?');
} else { }
$key = $value; if ($originalValue->value() === $value->value()) {
$value = null; continue;
} }
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([ $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key, 'key' => $key,
@@ -3384,6 +3460,8 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
'is_preview' => false, 'is_preview' => false,
]); ]);
} }
}
} }
if ($isApplication) { if ($isApplication) {
$branch = $originalResource->git_branch; $branch = $originalResource->git_branch;
@@ -3391,13 +3469,13 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$branch = "pull/{$pullRequestId}/head"; $branch = "pull/{$pullRequestId}/head";
} }
if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) { if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
$environment->put('COOLIFY_BRANCH', $branch); $coolifyEnvironments->put('COOLIFY_BRANCH', $branch);
} }
} }
// Add COOLIFY_CONTAINER_NAME to environment // Add COOLIFY_CONTAINER_NAME to environment
if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) { if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
$environment->put('COOLIFY_CONTAINER_NAME', $containerName); $coolifyEnvironments->put('COOLIFY_CONTAINER_NAME', $containerName);
} }
if ($isApplication) { if ($isApplication) {
@@ -3451,15 +3529,20 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} }
// Add COOLIFY_FQDN & COOLIFY_URL to environment // Add COOLIFY_FQDN & COOLIFY_URL to environment
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) { if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
$environment->put('COOLIFY_URL', $fqdns->implode(',')); $coolifyEnvironments->put('COOLIFY_URL', $fqdns->implode(','));
$urls = $fqdns->map(function ($fqdn) { $urls = $fqdns->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', ''); return str($fqdn)->replace('http://', '')->replace('https://', '');
}); });
$environment->put('COOLIFY_FQDN', $urls->implode(',')); $coolifyEnvironments->put('COOLIFY_FQDN', $urls->implode(','));
} }
add_coolify_default_environment_variables($resource, $environment, $resource->environment_variables); add_coolify_default_environment_variables($resource, $coolifyEnvironments, $resource->environment_variables);
if ($environment->count() > 0) {
$environment = $environment->filter(function ($value, $key) {
return ! str($key)->startsWith('SERVICE_FQDN_');
});
}
$serviceLabels = $labels->merge($defaultLabels); $serviceLabels = $labels->merge($defaultLabels);
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) { if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
if ($isApplication) { if ($isApplication) {
@@ -3545,17 +3628,19 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$payload = collect($service)->merge([ $payload = collect($service)->merge([
'container_name' => $containerName, 'container_name' => $containerName,
'restart' => $restart->value(), 'restart' => $restart->value(),
'networks' => $networks_temp,
'labels' => $serviceLabels, 'labels' => $serviceLabels,
]); ]);
if (! $use_network_mode) {
$payload['networks'] = $networks_temp;
}
if ($ports->count() > 0) { if ($ports->count() > 0) {
$payload['ports'] = $ports; $payload['ports'] = $ports;
} }
if ($volumesParsed->count() > 0) { if ($volumesParsed->count() > 0) {
$payload['volumes'] = $volumesParsed; $payload['volumes'] = $volumesParsed;
} }
if ($environment->count() > 0 || $coolifyDefinedEnvironments->count() > 0) { if ($environment->count() > 0 || $coolifyEnvironments->count() > 0) {
$payload['environment'] = $environment->merge($coolifyDefinedEnvironments); $payload['environment'] = $environment->merge($coolifyEnvironments);
} }
if ($logging) { if ($logging) {
$payload['logging'] = $logging; $payload['logging'] = $logging;

430
composer.lock generated

File diff suppressed because it is too large Load Diff

424
config/clockwork.php Normal file
View File

@@ -0,0 +1,424 @@
<?php
return [
/*
|------------------------------------------------------------------------------------------------------------------
| Enable Clockwork
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork is enabled by default only when your application is in debug mode. Here you can explicitly enable or
| disable Clockwork. When disabled, no data is collected and the api and web ui are inactive.
| Unless explicitly enabled, Clockwork only runs on localhost, *.local, *.test and *.wip domains.
|
*/
'enable' => env('CLOCKWORK_ENABLE', null),
/*
|------------------------------------------------------------------------------------------------------------------
| Features
|------------------------------------------------------------------------------------------------------------------
|
| You can enable or disable various Clockwork features here. Some features have additional settings (eg. slow query
| threshold for database queries).
|
*/
'features' => [
// Cache usage stats and cache queries including results
'cache' => [
'enabled' => env('CLOCKWORK_CACHE_ENABLED', true),
// Collect cache queries
'collect_queries' => env('CLOCKWORK_CACHE_QUERIES', true),
// Collect values from cache queries (high performance impact with a very high number of queries)
'collect_values' => env('CLOCKWORK_CACHE_COLLECT_VALUES', false)
],
// Database usage stats and queries
'database' => [
'enabled' => env('CLOCKWORK_DATABASE_ENABLED', true),
// Collect database queries (high performance impact with a very high number of queries)
'collect_queries' => env('CLOCKWORK_DATABASE_COLLECT_QUERIES', true),
// Collect details of models updates (high performance impact with a lot of model updates)
'collect_models_actions' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_ACTIONS', true),
// Collect details of retrieved models (very high performance impact with a lot of models retrieved)
'collect_models_retrieved' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_RETRIEVED', false),
// Query execution time threshold in milliseconds after which the query will be marked as slow
'slow_threshold' => env('CLOCKWORK_DATABASE_SLOW_THRESHOLD'),
// Collect only slow database queries
'slow_only' => env('CLOCKWORK_DATABASE_SLOW_ONLY', false),
// Detect and report duplicate queries
'detect_duplicate_queries' => env('CLOCKWORK_DATABASE_DETECT_DUPLICATE_QUERIES', false)
],
// Dispatched events
'events' => [
'enabled' => env('CLOCKWORK_EVENTS_ENABLED', true),
// Ignored events (framework events are ignored by default)
'ignored_events' => [
// App\Events\UserRegistered::class,
// 'user.registered'
],
],
// Laravel log (you can still log directly to Clockwork with laravel log disabled)
'log' => [
'enabled' => env('CLOCKWORK_LOG_ENABLED', true)
],
// Sent notifications
'notifications' => [
'enabled' => env('CLOCKWORK_NOTIFICATIONS_ENABLED', true),
],
// Performance metrics
'performance' => [
// Allow collecting of client metrics. Requires separate clockwork-browser npm package.
'client_metrics' => env('CLOCKWORK_PERFORMANCE_CLIENT_METRICS', true)
],
// Dispatched queue jobs
'queue' => [
'enabled' => env('CLOCKWORK_QUEUE_ENABLED', true)
],
// Redis commands
'redis' => [
'enabled' => env('CLOCKWORK_REDIS_ENABLED', true)
],
// Routes list
'routes' => [
'enabled' => env('CLOCKWORK_ROUTES_ENABLED', false),
// Collect only routes from particular namespaces (only application routes by default)
'only_namespaces' => [ 'App' ]
],
// Rendered views
'views' => [
'enabled' => env('CLOCKWORK_VIEWS_ENABLED', true),
// Collect views including view data (high performance impact with a high number of views)
'collect_data' => env('CLOCKWORK_VIEWS_COLLECT_DATA', false),
// Use Twig profiler instead of Laravel events for apps using laravel-twigbridge (more precise, but does
// not support collecting view data)
'use_twig_profiler' => env('CLOCKWORK_VIEWS_USE_TWIG_PROFILER', false)
]
],
/*
|------------------------------------------------------------------------------------------------------------------
| Enable web UI
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork comes with a web UI accessible via http://your.app/clockwork. Here you can enable or disable this
| feature. You can also set a custom path for the web UI.
|
*/
'web' => env('CLOCKWORK_WEB', true),
/*
|------------------------------------------------------------------------------------------------------------------
| Enable toolbar
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork can show a toolbar with basic metrics on all responses. Here you can enable or disable this feature.
| Requires a separate clockwork-browser npm library.
| For installation instructions see https://underground.works/clockwork/#docs-viewing-data
|
*/
'toolbar' => env('CLOCKWORK_TOOLBAR', true),
/*
|------------------------------------------------------------------------------------------------------------------
| HTTP requests collection
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork collects data about HTTP requests to your app. Here you can choose which requests should be collected.
|
*/
'requests' => [
// With on-demand mode enabled, Clockwork will only profile requests when the browser extension is open or you
// manually pass a "clockwork-profile" cookie or get/post data key.
// Optionally you can specify a "secret" that has to be passed as the value to enable profiling.
'on_demand' => env('CLOCKWORK_REQUESTS_ON_DEMAND', false),
// Collect only errors (requests with HTTP 4xx and 5xx responses)
'errors_only' => env('CLOCKWORK_REQUESTS_ERRORS_ONLY', false),
// Response time threshold in milliseconds after which the request will be marked as slow
'slow_threshold' => env('CLOCKWORK_REQUESTS_SLOW_THRESHOLD'),
// Collect only slow requests
'slow_only' => env('CLOCKWORK_REQUESTS_SLOW_ONLY', false),
// Sample the collected requests (e.g. set to 100 to collect only 1 in 100 requests)
'sample' => env('CLOCKWORK_REQUESTS_SAMPLE', false),
// List of URIs that should not be collected
'except' => [
'/horizon/.*', // Laravel Horizon requests
'/telescope/.*', // Laravel Telescope requests
'/_tt/.*', // Laravel Telescope toolbar
'/_debugbar/.*', // Laravel DebugBar requests
],
// List of URIs that should be collected, any other URI will not be collected if not empty
'only' => [
// '/api/.*'
],
// Don't collect OPTIONS requests, mostly used in the CSRF pre-flight requests and are rarely of interest
'except_preflight' => env('CLOCKWORK_REQUESTS_EXCEPT_PREFLIGHT', true)
],
/*
|------------------------------------------------------------------------------------------------------------------
| Artisan commands collection
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork can collect data about executed artisan commands. Here you can enable and configure which commands
| should be collected.
|
*/
'artisan' => [
// Enable or disable collection of executed Artisan commands
'collect' => env('CLOCKWORK_ARTISAN_COLLECT', false),
// List of commands that should not be collected (built-in commands are not collected by default)
'except' => [
// 'inspire'
],
// List of commands that should be collected, any other command will not be collected if not empty
'only' => [
// 'inspire'
],
// Enable or disable collection of command output
'collect_output' => env('CLOCKWORK_ARTISAN_COLLECT_OUTPUT', false),
// Enable or disable collection of built-in Laravel commands
'except_laravel_commands' => env('CLOCKWORK_ARTISAN_EXCEPT_LARAVEL_COMMANDS', true)
],
/*
|------------------------------------------------------------------------------------------------------------------
| Queue jobs collection
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork can collect data about executed queue jobs. Here you can enable and configure which queue jobs should
| be collected.
|
*/
'queue' => [
// Enable or disable collection of executed queue jobs
'collect' => env('CLOCKWORK_QUEUE_COLLECT', false),
// List of queue jobs that should not be collected
'except' => [
// App\Jobs\ExpensiveJob::class
],
// List of queue jobs that should be collected, any other queue job will not be collected if not empty
'only' => [
// App\Jobs\BuggyJob::class
]
],
/*
|------------------------------------------------------------------------------------------------------------------
| Tests collection
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork can collect data about executed tests. Here you can enable and configure which tests should be
| collected.
|
*/
'tests' => [
// Enable or disable collection of ran tests
'collect' => env('CLOCKWORK_TESTS_COLLECT', false),
// List of tests that should not be collected
'except' => [
// Tests\Unit\ExampleTest::class
]
],
/*
|------------------------------------------------------------------------------------------------------------------
| Enable data collection when Clockwork is disabled
|------------------------------------------------------------------------------------------------------------------
|
| You can enable this setting to collect data even when Clockwork is disabled, e.g. for future analysis.
|
*/
'collect_data_always' => env('CLOCKWORK_COLLECT_DATA_ALWAYS', false),
/*
|------------------------------------------------------------------------------------------------------------------
| Metadata storage
|------------------------------------------------------------------------------------------------------------------
|
| Configure how is the metadata collected by Clockwork stored. Three options are available:
| - files - A simple fast storage implementation storing data in one-per-request files.
| - sql - Stores requests in a sql database. Supports MySQL, PostgreSQL and SQLite. Requires PDO.
| - redis - Stores requests in redis. Requires phpredis.
*/
'storage' => env('CLOCKWORK_STORAGE', 'files'),
// Path where the Clockwork metadata is stored
'storage_files_path' => env('CLOCKWORK_STORAGE_FILES_PATH', storage_path('clockwork')),
// Compress the metadata files using gzip, trading a little bit of performance for lower disk usage
'storage_files_compress' => env('CLOCKWORK_STORAGE_FILES_COMPRESS', false),
// SQL database to use, can be a name of database configured in database.php or a path to a SQLite file
'storage_sql_database' => env('CLOCKWORK_STORAGE_SQL_DATABASE', storage_path('clockwork.sqlite')),
// SQL table name to use, the table is automatically created and updated when needed
'storage_sql_table' => env('CLOCKWORK_STORAGE_SQL_TABLE', 'clockwork'),
// Redis connection, name of redis connection or cluster configured in database.php
'storage_redis' => env('CLOCKWORK_STORAGE_REDIS', 'default'),
// Redis prefix for Clockwork keys ("clockwork" if not set)
'storage_redis_prefix' => env('CLOCKWORK_STORAGE_REDIS_PREFIX', 'clockwork'),
// Maximum lifetime of collected metadata in minutes, older requests will automatically be deleted, false to disable
'storage_expiration' => env('CLOCKWORK_STORAGE_EXPIRATION', 60 * 24 * 7),
/*
|------------------------------------------------------------------------------------------------------------------
| Authentication
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork can be configured to require authentication before allowing access to the collected data. This might be
| useful when the application is publicly accessible. Setting to true will enable a simple authentication with a
| pre-configured password. You can also pass a class name of a custom implementation.
|
*/
'authentication' => env('CLOCKWORK_AUTHENTICATION', false),
// Password for the simple authentication
'authentication_password' => env('CLOCKWORK_AUTHENTICATION_PASSWORD', 'VerySecretPassword'),
/*
|------------------------------------------------------------------------------------------------------------------
| Stack traces collection
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork can collect stack traces for log messages and certain data like database queries. Here you can set
| whether to collect stack traces, limit the number of collected frames and set further configuration. Collecting
| long stack traces considerably increases metadata size.
|
*/
'stack_traces' => [
// Enable or disable collecting of stack traces
'enabled' => env('CLOCKWORK_STACK_TRACES_ENABLED', true),
// Limit the number of frames to be collected
'limit' => env('CLOCKWORK_STACK_TRACES_LIMIT', 10),
// List of vendor names to skip when determining caller, common vendors are automatically added
'skip_vendors' => [
// 'phpunit'
],
// List of namespaces to skip when determining caller
'skip_namespaces' => [
// 'Laravel'
],
// List of class names to skip when determining caller
'skip_classes' => [
// App\CustomLog::class
]
],
/*
|------------------------------------------------------------------------------------------------------------------
| Serialization
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork serializes the collected data to json for storage and transfer. Here you can configure certain aspects
| of serialization. Serialization has a large effect on the cpu time and memory usage.
|
*/
// Maximum depth of serialized multi-level arrays and objects
'serialization_depth' => env('CLOCKWORK_SERIALIZATION_DEPTH', 10),
// A list of classes that will never be serialized (e.g. a common service container class)
'serialization_blackbox' => [
\Illuminate\Container\Container::class,
\Illuminate\Foundation\Application::class,
\Laravel\Lumen\Application::class
],
/*
|------------------------------------------------------------------------------------------------------------------
| Register helpers
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork comes with a "clock" global helper function. You can use this helper to quickly log something and to
| access the Clockwork instance.
|
*/
'register_helpers' => env('CLOCKWORK_REGISTER_HELPERS', true),
/*
|------------------------------------------------------------------------------------------------------------------
| Send headers for AJAX request
|------------------------------------------------------------------------------------------------------------------
|
| When trying to collect data, the AJAX method can sometimes fail if it is missing required headers. For example, an
| API might require a version number using Accept headers to route the HTTP request to the correct codebase.
|
*/
'headers' => [
// 'Accept' => 'application/vnd.com.whatever.v1+json',
],
/*
|------------------------------------------------------------------------------------------------------------------
| Server timing
|------------------------------------------------------------------------------------------------------------------
|
| Clockwork supports the W3C Server Timing specification, which allows for collecting a simple performance metrics
| in a cross-browser way. E.g. in Chrome, your app, database and timeline event timings will be shown in the Dev
| Tools network tab. This setting specifies the max number of timeline events that will be sent. Setting to false
| will disable the feature.
|
*/
'server_timing' => env('CLOCKWORK_SERVER_TIMING', 10)
];

View File

@@ -6,7 +6,9 @@ return [
'contact' => 'https://coolify.io/docs/contact', 'contact' => 'https://coolify.io/docs/contact',
], ],
'ssh' => [ 'ssh' => [
'mux_persist_time' => env('SSH_MUX_PERSIST_TIME', '1m'), // Using MUX
'mux_enabled' => env('MUX_ENABLED', env('SSH_MUX_ENABLED', true), true),
'mux_persist_time' => env('SSH_MUX_PERSIST_TIME', '1h'),
'connection_timeout' => 10, 'connection_timeout' => 10,
'server_interval' => 20, 'server_interval' => 20,
'command_timeout' => 7200, 'command_timeout' => 7200,

View File

@@ -7,7 +7,6 @@ return [
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),
'waitlist' => env('WAITLIST', false), 'waitlist' => env('WAITLIST', false),
'license_url' => 'https://licenses.coollabs.io', 'license_url' => 'https://licenses.coollabs.io',
'mux_enabled' => env('MUX_ENABLED', true),
'dev_webhook' => env('SERVEO_URL'), 'dev_webhook' => env('SERVEO_URL'),
'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false), 'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false),
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),

View File

@@ -7,7 +7,7 @@ return [
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.331', 'release' => '4.0.0-beta.335',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.331'; return '4.0.0-beta.335';

View File

@@ -0,0 +1,37 @@
<?php
use App\Models\ServerSetting;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('server_settings', function (Blueprint $table) {
$table->string('docker_cleanup_frequency')->default('0 0 * * *')->change();
});
$serverSettings = ServerSetting::all();
foreach ($serverSettings as $serverSetting) {
if ($serverSetting->force_docker_cleanup && $serverSetting->docker_cleanup_frequency === '*/10 * * * *') {
$serverSetting->docker_cleanup_frequency = '0 0 * * *';
$serverSetting->save();
}
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('server_settings', function (Blueprint $table) {
$table->string('docker_cleanup_frequency')->default('*/10 * * * *')->change();
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class UpdateServerSettingsDefaultTimezone extends Migration
{
public function up()
{
Schema::table('server_settings', function (Blueprint $table) {
$table->string('server_timezone')->default('UTC')->change();
});
DB::table('server_settings')
->whereNull('server_timezone')
->orWhere('server_timezone', '')
->update(['server_timezone' => 'UTC']);
}
public function down()
{
Schema::table('server_settings', function (Blueprint $table) {
$table->string('server_timezone')->default('')->change();
});
}
}

View File

@@ -19,6 +19,7 @@ services:
PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}"
volumes: volumes:
- .:/var/www/html/:cached - .:/var/www/html/:cached
- /data/coolify/backups/:/var/www/html/storage/app/backups
postgres: postgres:
pull_policy: always pull_policy: always
ports: ports:

View File

@@ -48,6 +48,7 @@ services:
- PUSHER_APP_SECRET - PUSHER_APP_SECRET
- AUTOUPDATE - AUTOUPDATE
- SELF_HOSTED - SELF_HOSTED
- SSH_MUX_ENABLED
- SSH_MUX_PERSIST_TIME - SSH_MUX_PERSIST_TIME
- FEEDBACK_DISCORD_WEBHOOK - FEEDBACK_DISCORD_WEBHOOK
- WAITLIST - WAITLIST

View File

@@ -45,7 +45,7 @@ services:
- PUSHER_APP_SECRET - PUSHER_APP_SECRET
- AUTOUPDATE=true - AUTOUPDATE=true
- SELF_HOSTED=true - SELF_HOSTED=true
- MUX_ENABLED=false - SSH_MUX_ENABLED=false
- IS_WINDOWS_DOCKER_DESKTOP=true - IS_WINDOWS_DOCKER_DESKTOP=true
ports: ports:
- "${APP_PORT:-8000}:80" - "${APP_PORT:-8000}:80"

View File

@@ -34,7 +34,7 @@ RUN if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \
chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack /root/.docker/cli-plugins/docker-buildx \ chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack /root/.docker/cli-plugins/docker-buildx \
;fi ;fi
COPY --from=minio/mc:RELEASE.2024-03-13T23-51-57Z /usr/bin/mc /usr/bin/mc COPY --from=minio/mc:RELEASE.2024-09-09T07-53-10Z /usr/bin/mc /usr/bin/mc
RUN chmod +x /usr/bin/mc RUN chmod +x /usr/bin/mc
ENTRYPOINT ["/sbin/tini", "--"] ENTRYPOINT ["/sbin/tini", "--"]

View File

@@ -37,6 +37,9 @@ RUN /bin/bash -c "if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \
curl -L https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \ curl -L https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \
;fi" ;fi"
COPY --from=minio/mc:RELEASE.2024-09-09T07-53-10Z /usr/bin/mc /usr/bin/mc
RUN chmod +x /usr/bin/mc
RUN { \ RUN { \
echo 'upload_max_filesize=256M'; \ echo 'upload_max_filesize=256M'; \
echo 'post_max_size=256M'; \ echo 'post_max_size=256M'; \

View File

@@ -68,3 +68,6 @@ RUN { \
echo 'upload_max_filesize=256M'; \ echo 'upload_max_filesize=256M'; \
echo 'post_max_size=256M'; \ echo 'post_max_size=256M'; \
} > /etc/php/current_version/cli/conf.d/upload-limits.ini } > /etc/php/current_version/cli/conf.d/upload-limits.ini
COPY --from=minio/mc:RELEASE.2024-09-09T07-53-10Z /usr/bin/mc /usr/bin/mc
RUN chmod +x /usr/bin/mc

View File

@@ -2831,7 +2831,7 @@ paths:
- -
name: uuid name: uuid
in: path in: path
description: 'Deployment Uuid' description: 'Deployment UUID'
required: true required: true
schema: schema:
type: string type: string
@@ -2879,7 +2879,7 @@ paths:
type: boolean type: boolean
responses: responses:
'200': '200':
description: "Get deployment(s) Uuid's" description: "Get deployment(s) UUID's"
content: content:
application/json: application/json:
schema: schema:
@@ -2993,7 +2993,7 @@ paths:
tags: tags:
- Projects - Projects
summary: List summary: List
description: 'list projects.' description: 'List projects.'
operationId: list-projects operationId: list-projects
responses: responses:
'200': '200':
@@ -3054,7 +3054,7 @@ paths:
tags: tags:
- Projects - Projects
summary: Get summary: Get
description: 'Get project by Uuid.' description: 'Get project by UUID.'
operationId: get-project-by-uuid operationId: get-project-by-uuid
parameters: parameters:
- -
@@ -3323,7 +3323,7 @@ paths:
- -
name: uuid name: uuid
in: path in: path
description: 'Private Key Uuid' description: 'Private Key UUID'
required: true required: true
schema: schema:
type: string type: string
@@ -3355,7 +3355,7 @@ paths:
- -
name: uuid name: uuid
in: path in: path
description: 'Private Key Uuid' description: 'Private Key UUID'
required: true required: true
schema: schema:
type: string type: string
@@ -3475,7 +3475,7 @@ paths:
- -
name: uuid name: uuid
in: path in: path
description: "Server's Uuid" description: "Server's UUID"
required: true required: true
schema: schema:
type: string type: string
@@ -3595,7 +3595,7 @@ paths:
- -
name: uuid name: uuid
in: path in: path
description: "Server's Uuid" description: "Server's UUID"
required: true required: true
schema: schema:
type: string type: string
@@ -3627,7 +3627,7 @@ paths:
- -
name: uuid name: uuid
in: path in: path
description: "Server's Uuid" description: "Server's UUID"
required: true required: true
schema: schema:
type: string type: string
@@ -3784,7 +3784,7 @@ paths:
type: string type: string
responses: responses:
'200': '200':
description: 'Get a service by Uuid.' description: 'Get a service by UUID.'
content: content:
application/json: application/json:
schema: schema:
@@ -3814,7 +3814,7 @@ paths:
type: string type: string
responses: responses:
'200': '200':
description: 'Delete a service by Uuid' description: 'Delete a service by UUID'
content: content:
application/json: application/json:
schema: schema:
@@ -3830,108 +3830,6 @@ paths:
security: security:
- -
bearerAuth: [] bearerAuth: []
'/services/{uuid}/start':
get:
tags:
- Services
summary: Start
description: 'Start service. `Post` request is also accepted.'
operationId: start-service-by-uuid
parameters:
-
name: uuid
in: path
description: 'UUID of the service.'
required: true
schema:
type: string
format: uuid
responses:
'200':
description: 'Start service.'
content:
application/json:
schema:
properties:
message: { type: string, example: 'Service starting request queued.' }
type: object
'401':
$ref: '#/components/responses/401'
'400':
$ref: '#/components/responses/400'
'404':
$ref: '#/components/responses/404'
security:
-
bearerAuth: []
'/services/{uuid}/stop':
get:
tags:
- Services
summary: Stop
description: 'Stop service. `Post` request is also accepted.'
operationId: stop-service-by-uuid
parameters:
-
name: uuid
in: path
description: 'UUID of the service.'
required: true
schema:
type: string
format: uuid
responses:
'200':
description: 'Stop service.'
content:
application/json:
schema:
properties:
message: { type: string, example: 'Service stopping request queued.' }
type: object
'401':
$ref: '#/components/responses/401'
'400':
$ref: '#/components/responses/400'
'404':
$ref: '#/components/responses/404'
security:
-
bearerAuth: []
'/services/{uuid}/restart':
get:
tags:
- Services
summary: Restart
description: 'Restart service. `Post` request is also accepted.'
operationId: restart-service-by-uuid
parameters:
-
name: uuid
in: path
description: 'UUID of the service.'
required: true
schema:
type: string
format: uuid
responses:
'200':
description: 'Restart service.'
content:
application/json:
schema:
properties:
message: { type: string, example: 'Service restaring request queued.' }
type: object
'401':
$ref: '#/components/responses/401'
'400':
$ref: '#/components/responses/400'
'404':
$ref: '#/components/responses/404'
security:
-
bearerAuth: []
'/services/{uuid}/envs': '/services/{uuid}/envs':
get: get:
tags: tags:
@@ -4182,6 +4080,108 @@ paths:
security: security:
- -
bearerAuth: [] bearerAuth: []
'/services/{uuid}/start':
get:
tags:
- Services
summary: Start
description: 'Start service. `Post` request is also accepted.'
operationId: start-service-by-uuid
parameters:
-
name: uuid
in: path
description: 'UUID of the service.'
required: true
schema:
type: string
format: uuid
responses:
'200':
description: 'Start service.'
content:
application/json:
schema:
properties:
message: { type: string, example: 'Service starting request queued.' }
type: object
'401':
$ref: '#/components/responses/401'
'400':
$ref: '#/components/responses/400'
'404':
$ref: '#/components/responses/404'
security:
-
bearerAuth: []
'/services/{uuid}/stop':
get:
tags:
- Services
summary: Stop
description: 'Stop service. `Post` request is also accepted.'
operationId: stop-service-by-uuid
parameters:
-
name: uuid
in: path
description: 'UUID of the service.'
required: true
schema:
type: string
format: uuid
responses:
'200':
description: 'Stop service.'
content:
application/json:
schema:
properties:
message: { type: string, example: 'Service stopping request queued.' }
type: object
'401':
$ref: '#/components/responses/401'
'400':
$ref: '#/components/responses/400'
'404':
$ref: '#/components/responses/404'
security:
-
bearerAuth: []
'/services/{uuid}/restart':
get:
tags:
- Services
summary: Restart
description: 'Restart service. `Post` request is also accepted.'
operationId: restart-service-by-uuid
parameters:
-
name: uuid
in: path
description: 'UUID of the service.'
required: true
schema:
type: string
format: uuid
responses:
'200':
description: 'Restart service.'
content:
application/json:
schema:
properties:
message: { type: string, example: 'Service restaring request queued.' }
type: object
'401':
$ref: '#/components/responses/401'
'400':
$ref: '#/components/responses/400'
'404':
$ref: '#/components/responses/404'
security:
-
bearerAuth: []
/teams: /teams:
get: get:
tags: tags:
@@ -4730,6 +4730,8 @@ components:
type: string type: string
name: name:
type: string type: string
description:
type: string
environments: environments:
description: 'The environments of the project.' description: 'The environments of the project.'
type: array type: array

View File

@@ -59,9 +59,9 @@ if [ $EUID != 0 ]; then
fi fi
case "$OS_TYPE" in case "$OS_TYPE" in
arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn) ;; arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn | alpine) ;;
*) *)
echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now." echo "This script only supports Debian, Redhat, Arch Linux, Alpine Linux, or SLES based operating systems for now."
exit exit
;; ;;
esac esac
@@ -90,6 +90,11 @@ case "$OS_TYPE" in
arch) arch)
pacman -Sy --noconfirm --needed curl wget git jq >/dev/null || true pacman -Sy --noconfirm --needed curl wget git jq >/dev/null || true
;; ;;
alpine)
sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
apk update >/dev/null
apk add curl wget git jq >/dev/null
;;
ubuntu | debian | raspbian) ubuntu | debian | raspbian)
apt-get update -y >/dev/null apt-get update -y >/dev/null
apt-get install -y curl wget git jq >/dev/null apt-get install -y curl wget git jq >/dev/null
@@ -172,8 +177,8 @@ if [ -x "$(command -v snap)" ]; then
fi fi
if ! [ -x "$(command -v docker)" ]; then if ! [ -x "$(command -v docker)" ]; then
# Almalinux case "$OS_TYPE" in
if [ "$OS_TYPE" == 'almalinux' ]; then "almalinux")
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
if ! [ -x "$(command -v docker)" ]; then if ! [ -x "$(command -v docker)" ]; then
@@ -182,12 +187,20 @@ if ! [ -x "$(command -v docker)" ]; then
fi fi
systemctl start docker systemctl start docker
systemctl enable docker systemctl enable docker
;;
"alpine")
apk add docker docker-cli-compose
rc-update add docker default
service docker start
if [ -x "$(command -v docker)" ]; then
echo "Docker installed successfully."
else else
set +e echo "Failed to install Docker with apk. Try to install it manually."
if ! [ -x "$(command -v docker)" ]; then echo "Please visit https://wiki.alpinelinux.org/wiki/Docker for more information."
echo "Docker is not installed. Installing Docker." exit
# Arch Linux fi
if [ "$OS_TYPE" = "arch" ]; then ;;
"arch")
pacman -Sy docker docker-compose --noconfirm pacman -Sy docker docker-compose --noconfirm
systemctl enable docker.service systemctl enable docker.service
if [ -x "$(command -v docker)" ]; then if [ -x "$(command -v docker)" ]; then
@@ -197,9 +210,8 @@ if ! [ -x "$(command -v docker)" ]; then
echo "Please visit https://wiki.archlinux.org/title/docker for more information." echo "Please visit https://wiki.archlinux.org/title/docker for more information."
exit exit
fi fi
else ;;
# Amazon Linux 2023 "amzn")
if [ "$OS_TYPE" = "amzn" ]; then
dnf install docker -y dnf install docker -y
DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker} DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker}
mkdir -p $DOCKER_CONFIG/cli-plugins mkdir -p $DOCKER_CONFIG/cli-plugins
@@ -210,11 +222,12 @@ if ! [ -x "$(command -v docker)" ]; then
if [ -x "$(command -v docker)" ]; then if [ -x "$(command -v docker)" ]; then
echo "Docker installed successfully." echo "Docker installed successfully."
else else
echo "Failed to install Docker with pacman. Try to install it manually." echo "Failed to install Docker with dnf. Try to install it manually."
echo "Please visit https://wiki.archlinux.org/title/docker for more information." echo "Please visit https://www.cyberciti.biz/faq/how-to-install-docker-on-amazon-linux-2/ for more information."
exit exit
fi fi
else ;;
*)
# Automated Docker installation # Automated Docker installation
curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh
if [ -x "$(command -v docker)" ]; then if [ -x "$(command -v docker)" ]; then
@@ -231,11 +244,7 @@ if ! [ -x "$(command -v docker)" ]; then
exit 1 exit 1
fi fi
fi fi
fi esac
fi
fi
set -e
fi
fi fi
echo -e "-------------" echo -e "-------------"
@@ -267,17 +276,50 @@ if ! jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify
fi fi
mv "$TEMP_FILE" /etc/docker/daemon.json mv "$TEMP_FILE" /etc/docker/daemon.json
restart_docker_service() {
# Check if systemctl is available
if command -v systemctl >/dev/null 2>&1; then
echo "Using systemctl to restart Docker..."
systemctl restart docker
if [ $? -eq 0 ]; then
echo "Docker restarted successfully using systemctl."
else
echo "Failed to restart Docker using systemctl."
return 1
fi
# Check if service command is available
elif command -v service >/dev/null 2>&1; then
echo "Using service command to restart Docker..."
service docker restart
if [ $? -eq 0 ]; then
echo "Docker restarted successfully using service."
else
echo "Failed to restart Docker using service."
return 1
fi
# If neither systemctl nor service is available
else
echo "Neither systemctl nor service command is available on this system."
return 1
fi
}
if [ -s /etc/docker/daemon.json.original-"$DATE" ]; then if [ -s /etc/docker/daemon.json.original-"$DATE" ]; then
DIFF=$(diff <(jq --sort-keys . /etc/docker/daemon.json) <(jq --sort-keys . /etc/docker/daemon.json.original-"$DATE")) DIFF=$(diff <(jq --sort-keys . /etc/docker/daemon.json) <(jq --sort-keys . /etc/docker/daemon.json.original-"$DATE"))
if [ "$DIFF" != "" ]; then if [ "$DIFF" != "" ]; then
echo "Docker configuration updated, restart docker daemon..." echo "Docker configuration updated, restart docker daemon..."
systemctl restart docker restart_docker_service
else else
echo "Docker configuration is up to date." echo "Docker configuration is up to date."
fi fi
else else
echo "Docker configuration updated, restart docker daemon..." echo "Docker configuration updated, restart docker daemon..."
systemctl restart docker restart_docker_service
fi fi
echo -e "-------------" echo -e "-------------"
@@ -296,28 +338,35 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh
# Copy .env.example if .env does not exist # Copy .env.example if .env does not exist
if [ ! -f $ENV_FILE ]; then if [ -f $ENV_FILE ]; then
cp /data/coolify/source/.env.production $ENV_FILE echo "File exists: $ENV_FILE"
cat $ENV_FILE
echo "Copying .env to .env-$DATE"
cp $ENV_FILE $ENV_FILE-$DATE
else
echo "File does not exist: $ENV_FILE"
echo "Copying .env.production to .env-$DATE"
cp /data/coolify/source/.env.production $ENV_FILE-$DATE
# Generate a secure APP_ID and APP_KEY # Generate a secure APP_ID and APP_KEY
sed -i "s|^APP_ID=.*|APP_ID=$(openssl rand -hex 16)|" "$ENV_FILE" sed -i "s|^APP_ID=.*|APP_ID=$(openssl rand -hex 16)|" "$ENV_FILE-$DATE"
sed -i "s|^APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|" "$ENV_FILE" sed -i "s|^APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|" "$ENV_FILE-$DATE"
# Generate a secure Postgres DB username and password # Generate a secure Postgres DB username and password
# Causes issues: database "random-user" does not exist # Causes issues: database "random-user" does not exist
# sed -i "s|^DB_USERNAME=.*|DB_USERNAME=$(openssl rand -hex 16)|" "$ENV_FILE" # sed -i "s|^DB_USERNAME=.*|DB_USERNAME=$(openssl rand -hex 16)|" "$ENV_FILE-$DATE"
sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE" sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE-$DATE"
# Generate a secure Redis password # Generate a secure Redis password
sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE" sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE-$DATE"
# Generate secure Pusher credentials # Generate secure Pusher credentials
sed -i "s|^PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|" "$ENV_FILE" sed -i "s|^PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE"
sed -i "s|^PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|" "$ENV_FILE" sed -i "s|^PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE"
sed -i "s|^PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|" "$ENV_FILE" sed -i "s|^PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE"
fi fi
# Merge .env and .env.production. New values will be added to .env # Merge .env and .env.production. New values will be added to .env
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env awk -F '=' '!seen[$1]++' "$ENV_FILE-$DATE" /data/coolify/source/.env.production > $ENV_FILE
if [ "$AUTOUPDATE" = "false" ]; then if [ "$AUTOUPDATE" = "false" ]; then
if ! grep -q "AUTOUPDATE=" /data/coolify/source/.env; then if ! grep -q "AUTOUPDATE=" /data/coolify/source/.env; then
@@ -350,7 +399,7 @@ if ! grep -qw "root@coolify" ~/.ssh/authorized_keys; then
fi fi
bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}" bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}"
rm -f $ENV_FILE-$DATE
echo "Waiting for 20 seconds for Coolify to be ready..." echo "Waiting for 20 seconds for Coolify to be ready..."
sleep 20 sleep 20

View File

@@ -11,7 +11,7 @@ curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.p
curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
# Merge .env and .env.production. New values will be added to .env # Merge .env and .env.production. New values will be added to .env
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env awk -F '=' '!seen[$1]++' /data/coolify/source/.env /data/coolify/source/.env.production > /data/coolify/source/.env.tmp && mv /data/coolify/source/.env.tmp /data/coolify/source/.env
# Check if PUSHER_APP_ID or PUSHER_APP_KEY or PUSHER_APP_SECRET is empty in /data/coolify/source/.env # Check if PUSHER_APP_ID or PUSHER_APP_KEY or PUSHER_APP_SECRET is empty in /data/coolify/source/.env
if grep -q "PUSHER_APP_ID=$" /data/coolify/source/.env; then if grep -q "PUSHER_APP_ID=$" /data/coolify/source/.env; then

View File

@@ -28,25 +28,33 @@
<x-highlighted text="Self-hosting with superpowers!" /></span> <x-highlighted text="Self-hosting with superpowers!" /></span>
</x-slot:question> </x-slot:question>
<x-slot:explanation> <x-slot:explanation>
<p><x-highlighted text="Task automation:" /> You don't need to manage your servers anymore. <p>
<x-highlighted text="Task automation:" /> You don't need to manage your servers anymore.
Coolify does Coolify does
it for you.</p> it for you.
<p><x-highlighted text="No vendor lock-in:" /> All configurations are stored on your servers, so </p>
everything works without a connection to Coolify (except integrations and automations).</p> <p>
<p><x-highlighted text="Monitoring:" />You can get notified on your favourite platforms <x-highlighted text="No vendor lock-in:" /> All configurations are stored on your servers, so
everything works without a connection to Coolify (except integrations and automations).
</p>
<p>
<x-highlighted text="Monitoring:" />You can get notified on your favourite platforms
(Discord, (Discord,
Telegram, Email, etc.) when something goes wrong, or an action is needed from your side.</p> Telegram, Email, etc.) when something goes wrong, or an action is needed from your side.
</p>
</x-slot:explanation> </x-slot:explanation>
<x-slot:actions> <x-slot:actions>
<x-forms.button class="justify-center lg:w-64 box-boarding" wire:click="explanation">Next <x-forms.button class="justify-center w-64 box-boarding" wire:click="explanation">Next
</x-forms.button> </x-forms.button>
</x-slot:actions> </x-slot:actions>
</x-boarding-step> </x-boarding-step>
@elseif ($currentState === 'select-server-type') @elseif ($currentState === 'select-server-type')
<x-boarding-step title="Server"> <x-boarding-step title="Server">
<x-slot:question> <x-slot:question>
Do you want to deploy your resources to your <x-highlighted text="Localhost" /> Do you want to deploy your resources to your
or to a <x-highlighted text="Remote Server" />? <x-highlighted text="Localhost" />
or to a
<x-highlighted text="Remote Server" />?
</x-slot:question> </x-slot:question>
<x-slot:actions> <x-slot:actions>
<x-forms.button class="justify-center w-64 box-boarding" wire:target="setServerType('localhost')" <x-forms.button class="justify-center w-64 box-boarding" wire:target="setServerType('localhost')"
@@ -56,32 +64,66 @@
<x-forms.button class="justify-center w-64 box-boarding " wire:target="setServerType('remote')" <x-forms.button class="justify-center w-64 box-boarding " wire:target="setServerType('remote')"
wire:click="setServerType('remote')">Remote Server wire:click="setServerType('remote')">Remote Server
</x-forms.button> </x-forms.button>
@if (!$serverReachable) @if (!$serverReachable)
Localhost is not reachable with the following public key. <div class="mt-6 p-4 border border-error rounded-lg text-gray-800 dark:text-gray-200">
<br /> <br /> <h2 class="text-lg font-bold mb-2">Server is not reachable</h2>
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for <p class="mb-4">Please check the connection details below and correct them if they are
user or skip the boarding process and add a new private key manually to Coolify and to the incorrect.</p>
server.
<br /> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
Check this <a target="_blank" class="underline" <x-forms.input placeholder="Default is 22" label="Port" id="remoteServerPort"
href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further wire:model="remoteServerPort" :value="$remoteServerPort" />
help. <div>
<x-forms.input readonly id="serverPublicKey"></x-forms.input> <x-forms.input placeholder="Default is root" label="User" id="remoteServerUser"
<x-forms.button class="lg:w-64 box-boarding" wire:target="setServerType('localhost')" wire:model="remoteServerUser" :value="$remoteServerUser" />
wire:click="setServerType('localhost')">Check again <p class="text-xs mt-1">
Non-root user is experimental:
<a class="font-bold underline" target="_blank"
href="https://coolify.io/docs/knowledge-base/server/non-root-user">docs</a>
</p>
</div>
</div>
<div class="mb-4">
<p class="mb-2">If the connection details are correct, please ensure:</p>
<ul class="list-disc list-inside">
<li>The correct public key is in your <code
class="bg-red-200 dark:bg-red-900 px-1 rounded">~/.ssh/authorized_keys</code>
file for the specified user</li>
<li>Or skip the boarding process and manually add a new private key to Coolify and
the server</li>
</ul>
</div>
<p class="mb-4">
For more help, check this <a target="_blank" class="underline font-semibold"
href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a>.
</p>
<x-forms.input readonly id="serverPublicKey" class="mb-4"
label="Current Public Key"></x-forms.input>
<x-forms.button class="w-full box-boarding" wire:click="saveAndValidateServer">
Check Again
</x-forms.button> </x-forms.button>
</div>
@endif @endif
</x-slot:actions> </x-slot:actions>
<x-slot:explanation> <x-slot:explanation>
<p>Servers are the main building blocks, as they will host your applications, databases, <p>Servers are the main building blocks, as they will host your applications, databases,
services, called resources. Any CPU intensive process will use the server's CPU where you services, called resources. Any CPU intensive process will use the server's CPU where you
are deploying your resources.</p> are deploying your resources.</p>
<p><x-highlighted text="Localhost" /> is the server where Coolify is running on. It is not <p>
<x-highlighted text="Localhost" /> is the server where Coolify is running on. It is not
recommended to use one server recommended to use one server
for everything.</p> for everything.
<p><x-highlighted text="A remote server" /> is a server reachable through SSH. It can be hosted </p>
<p>
<x-highlighted text="A remote server" /> is a server reachable through SSH. It can be hosted
at home, or from any cloud at home, or from any cloud
provider.</p> provider.
</p>
</x-slot:explanation> </x-slot:explanation>
</x-boarding-step> </x-boarding-step>
@elseif ($currentState === 'private-key') @elseif ($currentState === 'private-key')
@@ -126,10 +168,7 @@
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div> <div>
<x-forms.button class="justify-center w-64 box-boarding" wire:click="createNewServer">No <x-forms.button class="justify-center w-64 box-boarding" wire:click="createNewServer">No
(create (create one for me)
one
for
me)
</x-forms.button> </x-forms.button>
</div> </div>
<div> <div>
@@ -145,20 +184,48 @@
</div> </div>
</div> </div>
@if (!$serverReachable) @if (!$serverReachable)
This server is not reachable with the following public key. <div class="mt-6 p-4 bg-red-100 dark:bg-red-950 rounded-lg text-gray-800 dark:text-gray-200">
<br /> <br /> <h2 class="text-lg font-bold mb-2">Server is not reachable</h2>
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for <p class="mb-4">Please check the connection details below and correct them if they are
user or skip the boarding process and add a new private key manually to Coolify and to the incorrect.</p>
server.
<br /> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
Check this <a target="_blank" class="underline" <x-forms.input placeholder="Default is 22" label="Port" id="remoteServerPort"
href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further wire:model="remoteServerPort" :value="$remoteServerPort" />
help. <div>
<x-forms.input readonly id="serverPublicKey"></x-forms.input> <x-forms.input placeholder="Default is root" label="User" id="remoteServerUser"
<x-forms.button class="w-64 box-boarding" wire:target="validateServer" wire:model="remoteServerUser" :value="$remoteServerUser" />
wire:click="validateServer">Check <p class="text-xs mt-1">
again Non-root user is experimental:
<a class="font-bold underline" target="_blank"
href="https://coolify.io/docs/knowledge-base/server/non-root-user">docs</a>
</p>
</div>
</div>
<div class="mb-4">
<p class="mb-2">If the connection details are correct, please ensure:</p>
<ul class="list-disc list-inside">
<li>The correct public key is in your <code
class="bg-red-200 dark:bg-red-900 px-1 rounded">~/.ssh/authorized_keys</code>
file for the specified user</li>
<li>Or skip the boarding process and manually add a new private key to Coolify and
the server</li>
</ul>
</div>
<p class="mb-4">
For more help, check this <a target="_blank" class="underline font-semibold"
href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a>.
</p>
<x-forms.input readonly id="serverPublicKey" class="mb-4"
label="Current Public Key"></x-forms.input>
<x-forms.button class="w-full md:w-auto box-boarding" wire:click="saveAndValidateServer">
Check again
</x-forms.button> </x-forms.button>
</div>
@endif @endif
</x-slot:actions> </x-slot:actions>
<x-slot:explanation> <x-slot:explanation>
@@ -180,8 +247,8 @@
label="Name" id="privateKeyName" /> label="Name" id="privateKeyName" />
<x-forms.input placeholder="Description, so others will know more about this." <x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="privateKeyDescription" /> label="Description" id="privateKeyDescription" />
<x-forms.textarea required placeholder="-----BEGIN OPENSSH PRIVATE KEY-----" label="Private Key" <x-forms.textarea required placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
id="privateKey" /> label="Private Key" id="privateKey" />
@if ($privateKeyType === 'create') @if ($privateKeyType === 'create')
<x-forms.textarea rows="7" readonly label="Public Key" id="publicKey" /> <x-forms.textarea rows="7" readonly label="Public Key" id="publicKey" />
<span class="font-bold dark:text-warning">ACTION REQUIRED: Copy the 'Public Key' to your <span class="font-bold dark:text-warning">ACTION REQUIRED: Copy the 'Public Key' to your
@@ -209,27 +276,37 @@
<form wire:submit='saveServer' class="flex flex-col w-full gap-4 lg:pr-10"> <form wire:submit='saveServer' class="flex flex-col w-full gap-4 lg:pr-10">
<div class="flex flex-col gap-2 lg:flex-row"> <div class="flex flex-col gap-2 lg:flex-row">
<x-forms.input required placeholder="Choose a name for your Server. Could be anything." <x-forms.input required placeholder="Choose a name for your Server. Could be anything."
label="Name" id="remoteServerName" /> label="Name" id="remoteServerName" wire:model="remoteServerName" />
<x-forms.input placeholder="Description, so others will know more about this." <x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="remoteServerDescription" /> label="Description" id="remoteServerDescription"
wire:model="remoteServerDescription" />
</div> </div>
<div class="flex flex-col gap-2 lg:flex-row "> <div class="flex flex-col gap-2 lg:flex-row ">
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost" /> <x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost"
<x-forms.input required placeholder="Port number of your server. Default is 22." wire:model="remoteServerHost" />
label="Port" id="remoteServerPort" /> </div>
<div x-data="{ showAdvanced: false }" class="flex flex-col gap-2">
<button @click="showAdvanced = !showAdvanced" type="button"
class="text-left text-sm text-gray-600 dark:text-gray-300 hover:underline">
Advanced Settings
</button>
<div x-show="showAdvanced" class="flex flex-col gap-2 lg:flex-row">
<x-forms.input placeholder="Port number of your server. Default is 22." label="Port"
id="remoteServerPort" wire:model="remoteServerPort" />
<div class="w-full"> <div class="w-full">
<x-forms.input required placeholder="User to connect to your server. Default is root." <x-forms.input placeholder="Default is root." label="User"
label="User" id="remoteServerUser" /> id="remoteServerUser" wire:model="remoteServerUser" />
<div class="text-xs dark:text-warning text-coollabs ">Non-root user is experimental: <a <div class="text-xs text-gray-600 dark:text-gray-300">Non-root user is
class="font-bold underline" target="_blank" experimental: <a class="font-bold underline" target="_blank"
href="https://coolify.io/docs/knowledge-base/server/non-root-user">docs</a>. href="https://coolify.io/docs/knowledge-base/server/non-root-user">docs</a>.
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="lg:w-64"> <div class="lg:w-64">
<x-forms.checkbox <x-forms.checkbox
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>" helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="isCloudflareTunnel" label="Cloudflare Tunnel" /> id="isCloudflareTunnel" label="Cloudflare Tunnel" wire:model="isCloudflareTunnel" />
</div> </div>
<x-forms.button type="submit">Continue</x-forms.button> <x-forms.button type="submit">Continue</x-forms.button>
</form> </form>
@@ -329,5 +406,4 @@
<livewire:help /> <livewire:help />
</x-modal-input> </x-modal-input>
</div> </div>
</div>
</section> </section>

View File

@@ -59,9 +59,9 @@ if [ $EUID != 0 ]; then
fi fi
case "$OS_TYPE" in case "$OS_TYPE" in
arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn) ;; arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn | alpine) ;;
*) *)
echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now." echo "This script only supports Debian, Redhat, Arch Linux, Alpine Linux, or SLES based operating systems for now."
exit exit
;; ;;
esac esac
@@ -90,6 +90,11 @@ case "$OS_TYPE" in
arch) arch)
pacman -Sy --noconfirm --needed curl wget git jq >/dev/null || true pacman -Sy --noconfirm --needed curl wget git jq >/dev/null || true
;; ;;
alpine)
sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
apk update >/dev/null
apk add curl wget git jq >/dev/null
;;
ubuntu | debian | raspbian) ubuntu | debian | raspbian)
apt-get update -y >/dev/null apt-get update -y >/dev/null
apt-get install -y curl wget git jq >/dev/null apt-get install -y curl wget git jq >/dev/null
@@ -172,8 +177,8 @@ if [ -x "$(command -v snap)" ]; then
fi fi
if ! [ -x "$(command -v docker)" ]; then if ! [ -x "$(command -v docker)" ]; then
# Almalinux case "$OS_TYPE" in
if [ "$OS_TYPE" == 'almalinux' ]; then "almalinux")
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
if ! [ -x "$(command -v docker)" ]; then if ! [ -x "$(command -v docker)" ]; then
@@ -182,12 +187,20 @@ if ! [ -x "$(command -v docker)" ]; then
fi fi
systemctl start docker systemctl start docker
systemctl enable docker systemctl enable docker
;;
"alpine")
apk add docker docker-cli-compose
rc-update add docker default
service docker start
if [ -x "$(command -v docker)" ]; then
echo "Docker installed successfully."
else else
set +e echo "Failed to install Docker with apk. Try to install it manually."
if ! [ -x "$(command -v docker)" ]; then echo "Please visit https://wiki.alpinelinux.org/wiki/Docker for more information."
echo "Docker is not installed. Installing Docker." exit
# Arch Linux fi
if [ "$OS_TYPE" = "arch" ]; then ;;
"arch")
pacman -Sy docker docker-compose --noconfirm pacman -Sy docker docker-compose --noconfirm
systemctl enable docker.service systemctl enable docker.service
if [ -x "$(command -v docker)" ]; then if [ -x "$(command -v docker)" ]; then
@@ -197,9 +210,8 @@ if ! [ -x "$(command -v docker)" ]; then
echo "Please visit https://wiki.archlinux.org/title/docker for more information." echo "Please visit https://wiki.archlinux.org/title/docker for more information."
exit exit
fi fi
else ;;
# Amazon Linux 2023 "amzn")
if [ "$OS_TYPE" = "amzn" ]; then
dnf install docker -y dnf install docker -y
DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker} DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker}
mkdir -p $DOCKER_CONFIG/cli-plugins mkdir -p $DOCKER_CONFIG/cli-plugins
@@ -210,11 +222,12 @@ if ! [ -x "$(command -v docker)" ]; then
if [ -x "$(command -v docker)" ]; then if [ -x "$(command -v docker)" ]; then
echo "Docker installed successfully." echo "Docker installed successfully."
else else
echo "Failed to install Docker with pacman. Try to install it manually." echo "Failed to install Docker with dnf. Try to install it manually."
echo "Please visit https://wiki.archlinux.org/title/docker for more information." echo "Please visit https://www.cyberciti.biz/faq/how-to-install-docker-on-amazon-linux-2/ for more information."
exit exit
fi fi
else ;;
*)
# Automated Docker installation # Automated Docker installation
curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh
if [ -x "$(command -v docker)" ]; then if [ -x "$(command -v docker)" ]; then
@@ -231,11 +244,7 @@ if ! [ -x "$(command -v docker)" ]; then
exit 1 exit 1
fi fi
fi fi
fi esac
fi
fi
set -e
fi
fi fi
echo -e "-------------" echo -e "-------------"
@@ -267,17 +276,50 @@ if ! jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify
fi fi
mv "$TEMP_FILE" /etc/docker/daemon.json mv "$TEMP_FILE" /etc/docker/daemon.json
restart_docker_service() {
# Check if systemctl is available
if command -v systemctl >/dev/null 2>&1; then
echo "Using systemctl to restart Docker..."
systemctl restart docker
if [ $? -eq 0 ]; then
echo "Docker restarted successfully using systemctl."
else
echo "Failed to restart Docker using systemctl."
return 1
fi
# Check if service command is available
elif command -v service >/dev/null 2>&1; then
echo "Using service command to restart Docker..."
service docker restart
if [ $? -eq 0 ]; then
echo "Docker restarted successfully using service."
else
echo "Failed to restart Docker using service."
return 1
fi
# If neither systemctl nor service is available
else
echo "Neither systemctl nor service command is available on this system."
return 1
fi
}
if [ -s /etc/docker/daemon.json.original-"$DATE" ]; then if [ -s /etc/docker/daemon.json.original-"$DATE" ]; then
DIFF=$(diff <(jq --sort-keys . /etc/docker/daemon.json) <(jq --sort-keys . /etc/docker/daemon.json.original-"$DATE")) DIFF=$(diff <(jq --sort-keys . /etc/docker/daemon.json) <(jq --sort-keys . /etc/docker/daemon.json.original-"$DATE"))
if [ "$DIFF" != "" ]; then if [ "$DIFF" != "" ]; then
echo "Docker configuration updated, restart docker daemon..." echo "Docker configuration updated, restart docker daemon..."
systemctl restart docker restart_docker_service
else else
echo "Docker configuration is up to date." echo "Docker configuration is up to date."
fi fi
else else
echo "Docker configuration updated, restart docker daemon..." echo "Docker configuration updated, restart docker daemon..."
systemctl restart docker restart_docker_service
fi fi
echo -e "-------------" echo -e "-------------"
@@ -296,28 +338,35 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh
# Copy .env.example if .env does not exist # Copy .env.example if .env does not exist
if [ ! -f $ENV_FILE ]; then if [ -f $ENV_FILE ]; then
cp /data/coolify/source/.env.production $ENV_FILE echo "File exists: $ENV_FILE"
echo "Copying .env to .env-$DATE"
cp $ENV_FILE $ENV_FILE-$DATE
else
echo "File does not exist: $ENV_FILE"
echo "Copying .env.production to .env-$DATE"
cp /data/coolify/source/.env.production $ENV_FILE-$DATE
# Generate a secure APP_ID and APP_KEY # Generate a secure APP_ID and APP_KEY
sed -i "s|^APP_ID=.*|APP_ID=$(openssl rand -hex 16)|" "$ENV_FILE" sed -i "s|^APP_ID=.*|APP_ID=$(openssl rand -hex 16)|" "$ENV_FILE-$DATE"
sed -i "s|^APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|" "$ENV_FILE" sed -i "s|^APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|" "$ENV_FILE-$DATE"
# Generate a secure Postgres DB username and password # Generate a secure Postgres DB username and password
# Causes issues: database "random-user" does not exist # Causes issues: database "random-user" does not exist
# sed -i "s|^DB_USERNAME=.*|DB_USERNAME=$(openssl rand -hex 16)|" "$ENV_FILE" # sed -i "s|^DB_USERNAME=.*|DB_USERNAME=$(openssl rand -hex 16)|" "$ENV_FILE-$DATE"
sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE" sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE-$DATE"
# Generate a secure Redis password # Generate a secure Redis password
sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE" sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE-$DATE"
# Generate secure Pusher credentials # Generate secure Pusher credentials
sed -i "s|^PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|" "$ENV_FILE" sed -i "s|^PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE"
sed -i "s|^PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|" "$ENV_FILE" sed -i "s|^PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE"
sed -i "s|^PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|" "$ENV_FILE" sed -i "s|^PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE"
fi fi
# Merge .env and .env.production. New values will be added to .env # Merge .env and .env.production. New values will be added to .env
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env echo "Updating .env with new values (if necessary)..."
awk -F '=' '!seen[$1]++' "$ENV_FILE-$DATE" /data/coolify/source/.env.production > $ENV_FILE
if [ "$AUTOUPDATE" = "false" ]; then if [ "$AUTOUPDATE" = "false" ]; then
if ! grep -q "AUTOUPDATE=" /data/coolify/source/.env; then if ! grep -q "AUTOUPDATE=" /data/coolify/source/.env; then
@@ -350,9 +399,13 @@ if ! grep -qw "root@coolify" ~/.ssh/authorized_keys; then
fi fi
bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}" bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}"
rm -f $ENV_FILE-$DATE
echo "Waiting for 20 seconds for Coolify to be ready..." echo "Waiting for 20 seconds for Coolify to be ready..."
sleep 20 sleep 20
echo "Please visit http://$(curl -4s https://ifconfig.io):8000 to get started." echo "Please visit http://$(curl -4s https://ifconfig.io):8000 to get started."
echo -e "\nCongratulations! Your Coolify instance is ready to use.\n" echo -e "\nCongratulations! Your Coolify instance is ready to use.\n"
echo -e "Make sure you backup your /data/coolify/source/.env file to a safe location, outside of this server.\n"
cp /data/coolify/source/.env /data/coolify/source/.env.backup
echo -e "Your .env file has been copied to /data/coolify/source/.env.backup\n"

View File

@@ -11,8 +11,7 @@ curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.p
curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
# Merge .env and .env.production. New values will be added to .env # Merge .env and .env.production. New values will be added to .env
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env awk -F '=' '!seen[$1]++' /data/coolify/source/.env /data/coolify/source/.env.production > /data/coolify/source/.env.tmp && mv /data/coolify/source/.env.tmp /data/coolify/source/.env
# Check if PUSHER_APP_ID or PUSHER_APP_KEY or PUSHER_APP_SECRET is empty in /data/coolify/source/.env # Check if PUSHER_APP_ID or PUSHER_APP_KEY or PUSHER_APP_SECRET is empty in /data/coolify/source/.env
if grep -q "PUSHER_APP_ID=$" /data/coolify/source/.env; then if grep -q "PUSHER_APP_ID=$" /data/coolify/source/.env; then
sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env

View File

@@ -11,23 +11,23 @@ services:
- SERVICE_FQDN_ACTIVEPIECES - SERVICE_FQDN_ACTIVEPIECES
- AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
- AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY - AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
- AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js - AP_ENGINE_EXECUTABLE_PATH=${AP_ENGINE_EXECUTABLE_PATH:-dist/packages/engine/main.js}
- AP_ENVIRONMENT=prod - AP_ENVIRONMENT=${AP_ENVIRONMENT:-prod}
- AP_EXECUTION_MODE=UNSANDBOXED - AP_EXECUTION_MODE=${AP_EXECUTION_MODE:-UNSANDBOXED}
- AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES - AP_FRONTEND_URL=${SERVICE_FQDN_ACTIVEPIECES}
- AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT - AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT
- AP_POSTGRES_DATABASE=activepieces - AP_POSTGRES_DATABASE=${POSTGRES_DB:-activepieces}
- AP_POSTGRES_HOST=postgres - AP_POSTGRES_HOST=${POSTGRES_HOST:-postgres}
- AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - AP_POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- AP_POSTGRES_PORT=5432 - AP_POSTGRES_PORT=${POSTGRES_PORT:-5432}
- AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES - AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES
- AP_REDIS_HOST=redis - AP_REDIS_HOST=${REDIS_HOST:-redis}
- AP_REDIS_PORT=6379 - AP_REDIS_PORT=${REDIS_PORT:-6379}
- AP_SANDBOX_RUN_TIME_SECONDS=600 - AP_SANDBOX_RUN_TIME_SECONDS=${AP_SANDBOX_RUN_TIME_SECONDS:-600}
- AP_TELEMETRY_ENABLED=true - AP_TELEMETRY_ENABLED=${AP_TELEMETRY_ENABLED:-false}
- "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates" - AP_TEMPLATES_SOURCE_URL=${AP_TEMPLATES_SOURCE_URL:-https://cloud.activepieces.com/api/v1/flow-templates}
- AP_TRIGGER_DEFAULT_POLL_INTERVAL=5 - AP_TRIGGER_DEFAULT_POLL_INTERVAL=${AP_TRIGGER_DEFAULT_POLL_INTERVAL:-5}
- AP_WEBHOOK_TIMEOUT_SECONDS=30 - AP_WEBHOOK_TIMEOUT_SECONDS=${AP_WEBHOOK_TIMEOUT_SECONDS:-30}
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
@@ -41,9 +41,10 @@ services:
postgres: postgres:
image: "postgres:latest" image: "postgres:latest"
environment: environment:
- POSTGRES_DB=activepieces - POSTGRES_DB=${POSTGRES_DB:-activepieces}
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- POSTGRES_USER=$SERVICE_USER_POSTGRES - POSTGRES_USER=${SERVICE_USER_POSTGRES}
- POSTGRES_PORT=${POSTGRES_PORT:-5432}
volumes: volumes:
- "pg-data:/var/lib/postgresql/data" - "pg-data:/var/lib/postgresql/data"
healthcheck: healthcheck:

View File

@@ -8,11 +8,11 @@ services:
image: index.docker.io/appsmith/appsmith-ce:latest image: index.docker.io/appsmith/appsmith-ce:latest
environment: environment:
- SERVICE_FQDN_APPSMITH - SERVICE_FQDN_APPSMITH
- APPSMITH_MAIL_ENABLED=false - APPSMITH_MAIL_ENABLED=${APPSMITH_MAIL_ENABLED:-false}
- APPSMITH_DISABLE_TELEMETRY=true - APPSMITH_DISABLE_TELEMETRY=${APPSMITH_DISABLE_TELEMETRY:-false}
- APPSMITH_DISABLE_INTERCOM=true - APPSMITH_DISABLE_INTERCOM=${APPSMITH_DISABLE_INTERCOM:-true}
- APPSMITH_SENTRY_DSN= - APPSMITH_SENTRY_DSN=${APPSMITH_SENTRY_DSN}
- APPSMITH_SMART_LOOK_ID= - APPSMITH_SMART_LOOK_ID=${APPSMITH_SMART_LOOK_ID}
volumes: volumes:
- stacks-data:/appsmith-stacks - stacks-data:/appsmith-stacks
healthcheck: healthcheck:

View File

@@ -1,99 +0,0 @@
_APP_ENV=production
_APP_LOCALE=en
_APP_OPTIONS_ABUSE=enabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=
_APP_CONSOLE_WHITELIST_ROOT=enabled
_APP_CONSOLE_WHITELIST_EMAILS=
_APP_CONSOLE_WHITELIST_IPS=
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
_APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@appwrite.io
_APP_USAGE_STATS=enabled
_APP_LOGGING_PROVIDER=
_APP_LOGGING_CONFIG=
_APP_USAGE_AGGREGATION_INTERVAL=30
_APP_USAGE_TIMESERIES_INTERVAL=30
_APP_USAGE_DATABASE_INTERVAL=900
_APP_WORKER_PER_CORE=6
_APP_REDIS_HOST=appwrite-redis
_APP_REDIS_PORT=6379
_APP_REDIS_USER=
_APP_REDIS_PASS=
_APP_DB_HOST=appwrite-mariadb
_APP_DB_PORT=3306
_APP_DB_SCHEMA=appwrite
_APP_SMTP_HOST=
_APP_SMTP_PORT=
_APP_SMTP_SECURE=
_APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD=
_APP_SMS_PROVIDER=
_APP_SMS_FROM=
_APP_STORAGE_LIMIT=30000000
_APP_STORAGE_PREVIEW_LIMIT=20000000
_APP_STORAGE_ANTIVIRUS=disabled
_APP_STORAGE_ANTIVIRUS_HOST=appwrite-clamav
_APP_STORAGE_ANTIVIRUS_PORT=3310
_APP_STORAGE_DEVICE=local
_APP_STORAGE_S3_ACCESS_KEY=
_APP_STORAGE_S3_SECRET=
_APP_STORAGE_S3_REGION=us-east-1
_APP_STORAGE_S3_BUCKET=
_APP_STORAGE_DO_SPACES_ACCESS_KEY=
_APP_STORAGE_DO_SPACES_SECRET=
_APP_STORAGE_DO_SPACES_REGION=us-east-1
_APP_STORAGE_DO_SPACES_BUCKET=
_APP_STORAGE_BACKBLAZE_ACCESS_KEY=
_APP_STORAGE_BACKBLAZE_SECRET=
_APP_STORAGE_BACKBLAZE_REGION=us-west-004
_APP_STORAGE_BACKBLAZE_BUCKET=
_APP_STORAGE_LINODE_ACCESS_KEY=
_APP_STORAGE_LINODE_SECRET=
_APP_STORAGE_LINODE_REGION=eu-central-1
_APP_STORAGE_LINODE_BUCKET=
_APP_STORAGE_WASABI_ACCESS_KEY=
_APP_STORAGE_WASABI_SECRET=
_APP_STORAGE_WASABI_REGION=eu-central-1
_APP_STORAGE_WASABI_BUCKET=
_APP_FUNCTIONS_SIZE_LIMIT=30000000
_APP_FUNCTIONS_TIMEOUT=900
_APP_FUNCTIONS_BUILD_TIMEOUT=900
_APP_FUNCTIONS_CONTAINERS=10
_APP_FUNCTIONS_CPUS=0
_APP_FUNCTIONS_MEMORY=0
_APP_FUNCTIONS_MEMORY_SWAP=0
_APP_FUNCTIONS_RUNTIMES=node-20.0,php-8.2,python-3.11,ruby-3.2
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
DOCKERHUB_PULL_USERNAME=
DOCKERHUB_PULL_PASSWORD=
DOCKERHUB_PULL_EMAIL=
OPEN_RUNTIMES_NETWORK=appwrite_runtimes
_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes
_APP_DOCKER_HUB_USERNAME=
_APP_DOCKER_HUB_PASSWORD=
_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600
_APP_VCS_GITHUB_APP_NAME=
_APP_VCS_GITHUB_PRIVATE_KEY=
_APP_VCS_GITHUB_APP_ID=
_APP_VCS_GITHUB_CLIENT_ID=
_APP_VCS_GITHUB_CLIENT_SECRET=
_APP_VCS_GITHUB_WEBHOOK_SECRET=
_APP_MAINTENANCE_DELAY=
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_RETENTION_CACHE=2592000
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_GRAPHQL_MAX_BATCH_SIZE=10
_APP_GRAPHQL_MAX_COMPLEXITY=250
_APP_GRAPHQL_MAX_DEPTH=3
_APP_MIGRATIONS_FIREBASE_CLIENT_ID=
_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=
_APP_ASSISTANT_OPENAI_API_KEY=

View File

@@ -1,6 +1,5 @@
# documentation: https://appwrite.io # documentation: https://appwrite.io
# slogan: A backend-as-a-service platform that simplifies the web & mobile app development. # slogan: A backend-as-a-service platform that simplifies the web & mobile app development.
# env_file: appwrite.env
# tags: backend-as-a-service, platform # tags: backend-as-a-service, platform
# logo: svgs/appwrite.svg # logo: svgs/appwrite.svg
@@ -28,97 +27,97 @@ services:
- appwrite-redis - appwrite-redis
environment: environment:
- SERVICE_FQDN_APPWRITE=/ - SERVICE_FQDN_APPWRITE=/
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_LOCALE - _APP_LOCALE=${_APP_LOCALE:-en}
- _APP_CONSOLE_WHITELIST_ROOT - _APP_CONSOLE_WHITELIST_ROOT=${_APP_CONSOLE_WHITELIST_ROOT:-enabled}
- _APP_CONSOLE_WHITELIST_EMAILS - _APP_CONSOLE_WHITELIST_EMAILS=${_APP_CONSOLE_WHITELIST_EMAILS}
- _APP_CONSOLE_WHITELIST_IPS - _APP_CONSOLE_WHITELIST_IPS=${_APP_CONSOLE_WHITELIST_IPS}
- _APP_CONSOLE_HOSTNAMES - _APP_CONSOLE_HOSTNAMES=${_APP_CONSOLE_HOSTNAMES:-localhost,appwrite.io,*.appwrite.io}
- _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_NAME=${_APP_SYSTEM_EMAIL_NAME:-Appwrite}
- _APP_SYSTEM_EMAIL_ADDRESS - _APP_SYSTEM_EMAIL_ADDRESS=${_APP_SYSTEM_EMAIL_ADDRESS:-team@appwrite.io}
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=${_APP_SYSTEM_SECURITY_EMAIL_ADDRESS:-certs@appwrite.io}
- _APP_SYSTEM_RESPONSE_FORMAT - _APP_SYSTEM_RESPONSE_FORMAT=${_APP_SYSTEM_RESPONSE_FORMAT}
- _APP_OPTIONS_ABUSE - _APP_OPTIONS_ABUSE=${_APP_OPTIONS_ABUSE:-enabled}
- _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_FORCE_HTTPS=${_APP_OPTIONS_FORCE_HTTPS:-disabled}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_DOMAIN=$SERVICE_FQDN_APPWRITE - _APP_DOMAIN=$SERVICE_FQDN_APPWRITE
- _APP_DOMAIN_TARGET=$SERVICE_FQDN_APPWRITE - _APP_DOMAIN_TARGET=$SERVICE_FQDN_APPWRITE
- _APP_DOMAIN_FUNCTIONS=$SERVICE_FQDN_APPWRITE - _APP_DOMAIN_FUNCTIONS=$SERVICE_FQDN_APPWRITE
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER=$SERVICE_USER_MARIADB - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_DB_ROOT_PASS=$SERVICE_PASSWORD_MARIADBROOT - _APP_DB_ROOT_PASS=$SERVICE_PASSWORD_MARIADBROOT
- _APP_SMTP_HOST - _APP_SMTP_HOST=${_APP_SMTP_HOST}
- _APP_SMTP_PORT - _APP_SMTP_PORT=${_APP_SMTP_PORT}
- _APP_SMTP_SECURE - _APP_SMTP_SECURE=${_APP_SMTP_SECURE}
- _APP_SMTP_USERNAME - _APP_SMTP_USERNAME=${_APP_SMTP_USERNAME}
- _APP_SMTP_PASSWORD - _APP_SMTP_PASSWORD=${_APP_SMTP_PASSWORD}
- _APP_USAGE_STATS - _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_STORAGE_LIMIT - _APP_STORAGE_LIMIT=${_APP_STORAGE_LIMIT:-30000000}
- _APP_STORAGE_PREVIEW_LIMIT - _APP_STORAGE_PREVIEW_LIMIT=${_APP_STORAGE_PREVIEW_LIMIT:-20000000}
- _APP_STORAGE_ANTIVIRUS - _APP_STORAGE_ANTIVIRUS=${_APP_STORAGE_ANTIVIRUS:-disabled}
- _APP_STORAGE_ANTIVIRUS_HOST - _APP_STORAGE_ANTIVIRUS_HOST=${_APP_STORAGE_ANTIVIRUS_HOST:-appwrite-clamav}
- _APP_STORAGE_ANTIVIRUS_PORT - _APP_STORAGE_ANTIVIRUS_PORT=${_APP_STORAGE_ANTIVIRUS_PORT:-3310}
- _APP_STORAGE_DEVICE - _APP_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local}
- _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY}
- _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET - _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION - _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1}
- _APP_STORAGE_DO_SPACES_BUCKET - _APP_STORAGE_DO_SPACES_BUCKET=${_APP_STORAGE_DO_SPACES_BUCKET}
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY - _APP_STORAGE_BACKBLAZE_ACCESS_KEY=${_APP_STORAGE_BACKBLAZE_ACCESS_KEY}
- _APP_STORAGE_BACKBLAZE_SECRET - _APP_STORAGE_BACKBLAZE_SECRET=${_APP_STORAGE_BACKBLAZE_SECRET}
- _APP_STORAGE_BACKBLAZE_REGION - _APP_STORAGE_BACKBLAZE_REGION=${_APP_STORAGE_BACKBLAZE_REGION:-us-west-004}
- _APP_STORAGE_BACKBLAZE_BUCKET - _APP_STORAGE_BACKBLAZE_BUCKET=${_APP_STORAGE_BACKBLAZE_BUCKET}
- _APP_STORAGE_LINODE_ACCESS_KEY - _APP_STORAGE_LINODE_ACCESS_KEY=${_APP_STORAGE_LINODE_ACCESS_KEY}
- _APP_STORAGE_LINODE_SECRET - _APP_STORAGE_LINODE_SECRET=${_APP_STORAGE_LINODE_SECRET}
- _APP_STORAGE_LINODE_REGION - _APP_STORAGE_LINODE_REGION=${_APP_STORAGE_LINODE_REGION:-eu-central-1}
- _APP_STORAGE_LINODE_BUCKET - _APP_STORAGE_LINODE_BUCKET=${_APP_STORAGE_LINODE_BUCKET}
- _APP_STORAGE_WASABI_ACCESS_KEY - _APP_STORAGE_WASABI_ACCESS_KEY=${_APP_STORAGE_WASABI_ACCESS_KEY}
- _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_SECRET=${_APP_STORAGE_WASABI_SECRET}
- _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1}
- _APP_STORAGE_WASABI_BUCKET - _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
- _APP_FUNCTIONS_SIZE_LIMIT - _APP_FUNCTIONS_SIZE_LIMIT=${_APP_FUNCTIONS_SIZE_LIMIT:-30000000}
- _APP_FUNCTIONS_TIMEOUT - _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT - _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0}
- _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0}
- _APP_FUNCTIONS_RUNTIMES - _APP_FUNCTIONS_RUNTIMES=${_APP_FUNCTIONS_RUNTIMES:-node-20.0,php-8.2,python-3.11,ruby-3.2}
- _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE - _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST - _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_INTERVAL=${_APP_MAINTENANCE_INTERVAL:-86400}
- _APP_MAINTENANCE_DELAY - _APP_MAINTENANCE_DELAY=${_APP_MAINTENANCE_DELAY}
- _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_EXECUTION=${_APP_MAINTENANCE_RETENTION_EXECUTION:-1209600}
- _APP_MAINTENANCE_RETENTION_CACHE - _APP_MAINTENANCE_RETENTION_CACHE=${_APP_MAINTENANCE_RETENTION_CACHE:-2592000}
- _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600}
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=${_APP_MAINTENANCE_RETENTION_USAGE_HOURLY:-8640000}
- _APP_MAINTENANCE_RETENTION_SCHEDULES - _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400}
- _APP_SMS_PROVIDER - _APP_SMS_PROVIDER=${_APP_SMS_PROVIDER}
- _APP_SMS_FROM - _APP_SMS_FROM=${_APP_SMS_FROM}
- _APP_GRAPHQL_MAX_BATCH_SIZE - _APP_GRAPHQL_MAX_BATCH_SIZE=${_APP_GRAPHQL_MAX_BATCH_SIZE:-10}
- _APP_GRAPHQL_MAX_COMPLEXITY - _APP_GRAPHQL_MAX_COMPLEXITY=${_APP_GRAPHQL_MAX_COMPLEXITY:-250}
- _APP_GRAPHQL_MAX_DEPTH - _APP_GRAPHQL_MAX_DEPTH=${_APP_GRAPHQL_MAX_DEPTH:-3}
- _APP_VCS_GITHUB_APP_NAME - _APP_VCS_GITHUB_APP_NAME=${_APP_VCS_GITHUB_APP_NAME}
- _APP_VCS_GITHUB_PRIVATE_KEY - _APP_VCS_GITHUB_PRIVATE_KEY=${_APP_VCS_GITHUB_PRIVATE_KEY}
- _APP_VCS_GITHUB_APP_ID - _APP_VCS_GITHUB_APP_ID=${_APP_VCS_GITHUB_APP_ID}
- _APP_VCS_GITHUB_WEBHOOK_SECRET - _APP_VCS_GITHUB_WEBHOOK_SECRET=${_APP_VCS_GITHUB_WEBHOOK_SECRET}
- _APP_VCS_GITHUB_CLIENT_SECRET - _APP_VCS_GITHUB_CLIENT_SECRET=${_APP_VCS_GITHUB_CLIENT_SECRET}
- _APP_VCS_GITHUB_CLIENT_ID - _APP_VCS_GITHUB_CLIENT_ID=${_APP_VCS_GITHUB_CLIENT_ID}
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_ID=${_APP_MIGRATIONS_FIREBASE_CLIENT_ID}
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=${_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET}
- _APP_ASSISTANT_OPENAI_API_KEY - _APP_ASSISTANT_OPENAI_API_KEY=${_APP_ASSISTANT_OPENAI_API_KEY}
appwrite-realtime: appwrite-realtime:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -129,22 +128,22 @@ services:
- appwrite-redis - appwrite-redis
environment: environment:
- SERVICE_FQDN_APPWRITE=/v1/realtime - SERVICE_FQDN_APPWRITE=/v1/realtime
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPTIONS_ABUSE - _APP_OPTIONS_ABUSE=${_APP_OPTIONS_ABUSE:-enabled}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_USAGE_STATS - _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-audits: appwrite-worker-audits:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -155,20 +154,20 @@ services:
- appwrite-redis - appwrite-redis
- appwrite-mariadb - appwrite-mariadb
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-webhooks: appwrite-worker-webhooks:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -179,16 +178,16 @@ services:
- appwrite-redis - appwrite-redis
- appwrite-mariadb - appwrite-mariadb
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=${_APP_SYSTEM_SECURITY_EMAIL_ADDRESS:-certs@appwrite.io}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-deletes: appwrite-worker-deletes:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -205,43 +204,43 @@ services:
- appwrite-builds:/storage/builds:rw - appwrite-builds:/storage/builds:rw
- appwrite-certificates:/storage/certificates:rw - appwrite-certificates:/storage/certificates:rw
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_STORAGE_DEVICE - _APP_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local}
- _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY:-local}
- _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET - _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION - _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1}
- _APP_STORAGE_DO_SPACES_BUCKET - _APP_STORAGE_DO_SPACES_BUCKET=${_APP_STORAGE_DO_SPACES_BUCKET}
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY - _APP_STORAGE_BACKBLAZE_ACCESS_KEY=${_APP_STORAGE_BACKBLAZE_ACCESS_KEY}
- _APP_STORAGE_BACKBLAZE_SECRET - _APP_STORAGE_BACKBLAZE_SECRET=${_APP_STORAGE_BACKBLAZE_SECRET}
- _APP_STORAGE_BACKBLAZE_REGION - _APP_STORAGE_BACKBLAZE_REGION=${_APP_STORAGE_BACKBLAZE_REGION:-us-west-004}
- _APP_STORAGE_BACKBLAZE_BUCKET - _APP_STORAGE_BACKBLAZE_BUCKET=${_APP_STORAGE_BACKBLAZE_BUCKET}
- _APP_STORAGE_LINODE_ACCESS_KEY - _APP_STORAGE_LINODE_ACCESS_KEY=${_APP_STORAGE_LINODE_ACCESS_KEY}
- _APP_STORAGE_LINODE_SECRET - _APP_STORAGE_LINODE_SECRET=${_APP_STORAGE_LINODE_SECRET}
- _APP_STORAGE_LINODE_REGION - _APP_STORAGE_LINODE_REGION=${_APP_STORAGE_LINODE_REGION:-eu-central-1}
- _APP_STORAGE_LINODE_BUCKET - _APP_STORAGE_LINODE_BUCKET=${_APP_STORAGE_LINODE_BUCKET}
- _APP_STORAGE_WASABI_ACCESS_KEY - _APP_STORAGE_WASABI_ACCESS_KEY=${_APP_STORAGE_WASABI_ACCESS_KEY}
- _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_SECRET=${_APP_STORAGE_WASABI_SECRET}
- _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1}
- _APP_STORAGE_WASABI_BUCKET - _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_EXECUTOR_SECRET - _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST - _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
appwrite-worker-databases: appwrite-worker-databases:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -252,20 +251,20 @@ services:
- appwrite-redis - appwrite-redis
- appwrite-mariadb - appwrite-mariadb
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-builds: appwrite-worker-builds:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -279,52 +278,52 @@ services:
- appwrite-functions:/storage/functions:rw - appwrite-functions:/storage/functions:rw
- appwrite-builds:/storage/builds:rw - appwrite-builds:/storage/builds:rw
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_EXECUTOR_SECRET - _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST - _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_VCS_GITHUB_APP_NAME - _APP_VCS_GITHUB_APP_NAME=${_APP_VCS_GITHUB_APP_NAME}
- _APP_VCS_GITHUB_PRIVATE_KEY - _APP_VCS_GITHUB_PRIVATE_KEY=${_APP_VCS_GITHUB_PRIVATE_KEY}
- _APP_VCS_GITHUB_APP_ID - _APP_VCS_GITHUB_APP_ID=${_APP_VCS_GITHUB_APP_ID}
- _APP_FUNCTIONS_TIMEOUT - _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT - _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0}
- _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0}
- _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_FORCE_HTTPS=${_APP_OPTIONS_FORCE_HTTPS:-disabled}
- _APP_DOMAIN - _APP_DOMAIN=$SERVICE_FQDN_APPWRITE
- _APP_STORAGE_DEVICE - _APP_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local}
- _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY:-local}
- _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET - _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION - _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1}
- _APP_STORAGE_DO_SPACES_BUCKET - _APP_STORAGE_DO_SPACES_BUCKET=${_APP_STORAGE_DO_SPACES_BUCKET}
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY - _APP_STORAGE_BACKBLAZE_ACCESS_KEY=${_APP_STORAGE_BACKBLAZE_ACCESS_KEY}
- _APP_STORAGE_BACKBLAZE_SECRET - _APP_STORAGE_BACKBLAZE_SECRET=${_APP_STORAGE_BACKBLAZE_SECRET}
- _APP_STORAGE_BACKBLAZE_REGION - _APP_STORAGE_BACKBLAZE_REGION=${_APP_STORAGE_BACKBLAZE_REGION:-us-west-004}
- _APP_STORAGE_BACKBLAZE_BUCKET - _APP_STORAGE_BACKBLAZE_BUCKET=${_APP_STORAGE_BACKBLAZE_BUCKET}
- _APP_STORAGE_LINODE_ACCESS_KEY - _APP_STORAGE_LINODE_ACCESS_KEY=${_APP_STORAGE_LINODE_ACCESS_KEY}
- _APP_STORAGE_LINODE_SECRET - _APP_STORAGE_LINODE_SECRET=${_APP_STORAGE_LINODE_SECRET}
- _APP_STORAGE_LINODE_REGION - _APP_STORAGE_LINODE_REGION=${_APP_STORAGE_LINODE_REGION:-eu-central-1}
- _APP_STORAGE_LINODE_BUCKET - _APP_STORAGE_LINODE_BUCKET=${_APP_STORAGE_LINODE_BUCKET}
- _APP_STORAGE_WASABI_ACCESS_KEY - _APP_STORAGE_WASABI_ACCESS_KEY=${_APP_STORAGE_WASABI_ACCESS_KEY}
- _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_SECRET=${_APP_STORAGE_WASABI_SECRET}
- _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1}
- _APP_STORAGE_WASABI_BUCKET - _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
appwrite-worker-certificates: appwrite-worker-certificates:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -338,24 +337,24 @@ services:
- appwrite-config:/storage/config:rw - appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw - appwrite-certificates:/storage/certificates:rw
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_DOMAIN - _APP_DOMAIN=$SERVICE_FQDN_APPWRITE
- _APP_DOMAIN_TARGET - _APP_DOMAIN_TARGET=$SERVICE_FQDN_APPWRITE
- _APP_DOMAIN_FUNCTIONS - _APP_DOMAIN_FUNCTIONS=$SERVICE_FQDN_APPWRITE
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=${_APP_SYSTEM_SECURITY_EMAIL_ADDRESS:-certs@appwrite.io}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-functions: appwrite-worker-functions:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -367,29 +366,30 @@ services:
- appwrite-mariadb - appwrite-mariadb
- openruntimes-executor - openruntimes-executor
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_FUNCTIONS_TIMEOUT - _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT - _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0}
- _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0}
- _APP_EXECUTOR_SECRET - _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST - _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_USAGE_STATS - _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_DOCKER_HUB_USERNAME - _APP_DOCKER_HUB_USERNAME=${_APP_DOCKER_HUB_USERNAME}
- _APP_DOCKER_HUB_PASSWORD - _APP_DOCKER_HUB_PASSWORD=${_APP_DOCKER_HUB_PASSWORD}
- _APP_LOGGING_CONFIG - _APP_DOCKER_HUB_EMAIL=${_APP_DOCKER_HUB_EMAIL}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
appwrite-worker-mails: appwrite-worker-mails:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -399,22 +399,22 @@ services:
depends_on: depends_on:
- appwrite-redis - appwrite-redis
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_NAME=${_APP_SYSTEM_EMAIL_NAME:-Appwrite}
- _APP_SYSTEM_EMAIL_ADDRESS - _APP_SYSTEM_EMAIL_ADDRESS=${_APP_SYSTEM_EMAIL_ADDRESS:-team@appwrite.io}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_SMTP_HOST - _APP_SMTP_HOST=${_APP_SMTP_HOST}
- _APP_SMTP_PORT - _APP_SMTP_PORT=${_APP_SMTP_PORT}
- _APP_SMTP_SECURE - _APP_SMTP_SECURE=${_APP_SMTP_SECURE}
- _APP_SMTP_USERNAME - _APP_SMTP_USERNAME=${_APP_SMTP_USERNAME}
- _APP_SMTP_PASSWORD - _APP_SMTP_PASSWORD=${_APP_SMTP_PASSWORD}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-messaging: appwrite-worker-messaging:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -424,22 +424,22 @@ services:
depends_on: depends_on:
- appwrite-redis - appwrite-redis
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_SMS_FROM - _APP_SMS_FROM=${_APP_SMS_FROM}
- _APP_SMS_PROVIDER - _APP_SMS_PROVIDER=${_APP_SMS_PROVIDER}
appwrite-worker-migrations: appwrite-worker-migrations:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -449,25 +449,25 @@ services:
depends_on: depends_on:
- appwrite-mariadb - appwrite-mariadb
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_DOMAIN - _APP_DOMAIN=$SERVICE_FQDN_APPWRITE
- _APP_DOMAIN_TARGET - _APP_DOMAIN_TARGET=$SERVICE_FQDN_APPWRITE
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=${_APP_SYSTEM_SECURITY_EMAIL_ADDRESS:-certs@appwrite.io}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_ID=${_APP_MIGRATIONS_FIREBASE_CLIENT_ID}
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=${_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET}
appwrite-maintenance: appwrite-maintenance:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -477,28 +477,28 @@ services:
depends_on: depends_on:
- appwrite-redis - appwrite-redis
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_DOMAIN - _APP_DOMAIN=$SERVICE_FQDN_APPWRITE
- _APP_DOMAIN_TARGET - _APP_DOMAIN_TARGET=$SERVICE_FQDN_APPWRITE
- _APP_DOMAIN_FUNCTIONS - _APP_DOMAIN_FUNCTIONS=$SERVICE_FQDN_APPWRITE
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_INTERVAL=${_APP_MAINTENANCE_INTERVAL}
- _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_EXECUTION=${_APP_MAINTENANCE_RETENTION_EXECUTION}
- _APP_MAINTENANCE_RETENTION_CACHE - _APP_MAINTENANCE_RETENTION_CACHE=${_APP_MAINTENANCE_RETENTION_CACHE:-2592000}
- _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600}
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=${_APP_MAINTENANCE_RETENTION_USAGE_HOURLY:-8640000}
- _APP_MAINTENANCE_RETENTION_SCHEDULES - _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400}
appwrite-worker-usage: appwrite-worker-usage:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -510,22 +510,22 @@ services:
- appwrite-redis - appwrite-redis
- appwrite-mariadb - appwrite-mariadb
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_USAGE_STATS - _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_USAGE_AGGREGATION_INTERVAL - _APP_USAGE_AGGREGATION_INTERVAL=${_APP_USAGE_AGGREGATION_INTERVAL:-30}
appwrite-worker-usage-dump: appwrite-worker-usage-dump:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -536,22 +536,22 @@ services:
- appwrite-redis - appwrite-redis
- appwrite-mariadb - appwrite-mariadb
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_USAGE_STATS - _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_LOGGING_PROVIDER - _APP_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- _APP_LOGGING_CONFIG - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_USAGE_AGGREGATION_INTERVAL - _APP_USAGE_AGGREGATION_INTERVAL=${_APP_USAGE_AGGREGATION_INTERVAL:-30}
appwrite-scheduler-functions: appwrite-scheduler-functions:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -563,18 +563,18 @@ services:
- appwrite-mariadb - appwrite-mariadb
- appwrite-redis - appwrite-redis
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
appwrite-scheduler-messages: appwrite-scheduler-messages:
image: appwrite/appwrite:1.5 image: appwrite/appwrite:1.5
@@ -586,18 +586,18 @@ services:
- appwrite-mariadb - appwrite-mariadb
- appwrite-redis - appwrite-redis
environment: environment:
- _APP_ENV - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1 - _APP_OPENSSL_KEY_V1=${_APP_OPENSSL_KEY_V1}
- _APP_REDIS_HOST - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER - _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_DB_HOST - _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT - _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA - _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
appwrite-assistant: appwrite-assistant:
image: appwrite/assistant:0.4.0 image: appwrite/assistant:0.4.0
@@ -616,37 +616,37 @@ services:
- appwrite-functions:/storage/functions:rw - appwrite-functions:/storage/functions:rw
- /tmp:/tmp:rw - /tmp:/tmp:rw
environment: environment:
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD - OPR_EXECUTOR_INACTIVE_TRESHOLD=${_APP_FUNCTIONS_INACTIVE_THRESHOLD}
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL - OPR_EXECUTOR_MAINTENANCE_INTERVAL=${_APP_FUNCTIONS_MAINTENANCE_INTERVAL}
- OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK - OPR_EXECUTOR_NETWORK=${_APP_FUNCTIONS_RUNTIMES_NETWORK}
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME - OPR_EXECUTOR_DOCKER_HUB_USERNAME=${_APP_DOCKER_HUB_USERNAME}
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD - OPR_EXECUTOR_DOCKER_HUB_PASSWORD=${_APP_DOCKER_HUB_PASSWORD}
- OPR_EXECUTOR_ENV=$_APP_ENV - OPR_EXECUTOR_ENV=${_APP_ENV:-production}
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES - OPR_EXECUTOR_RUNTIMES=${_APP_FUNCTIONS_RUNTIMES}
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET - OPR_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- OPR_EXECUTOR_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER - OPR_EXECUTOR_LOGGING_PROVIDER=${_APP_LOGGING_PROVIDER}
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG - OPR_EXECUTOR_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE - OPR_EXECUTOR_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local}
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY:-local}
- OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET - OPR_EXECUTOR_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION - OPR_EXECUTOR_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION}
- OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET - OPR_EXECUTOR_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY - OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION}
- OPR_EXECUTOR_STORAGE_DO_SPACES_BUCKET=$_APP_STORAGE_DO_SPACES_BUCKET - OPR_EXECUTOR_STORAGE_DO_SPACES_BUCKET=${_APP_STORAGE_DO_SPACES_BUCKET}
- OPR_EXECUTOR_STORAGE_BACKBLAZE_ACCESS_KEY=$_APP_STORAGE_BACKBLAZE_ACCESS_KEY - OPR_EXECUTOR_STORAGE_BACKBLAZE_ACCESS_KEY=${_APP_STORAGE_BACKBLAZE_ACCESS_KEY}
- OPR_EXECUTOR_STORAGE_BACKBLAZE_SECRET=$_APP_STORAGE_BACKBLAZE_SECRET - OPR_EXECUTOR_STORAGE_BACKBLAZE_SECRET=${_APP_STORAGE_BACKBLAZE_SECRET}
- OPR_EXECUTOR_STORAGE_BACKBLAZE_REGION=$_APP_STORAGE_BACKBLAZE_REGION - OPR_EXECUTOR_STORAGE_BACKBLAZE_REGION=${_APP_STORAGE_BACKBLAZE_REGION}
- OPR_EXECUTOR_STORAGE_BACKBLAZE_BUCKET=$_APP_STORAGE_BACKBLAZE_BUCKET - OPR_EXECUTOR_STORAGE_BACKBLAZE_BUCKET=${_APP_STORAGE_BACKBLAZE_BUCKET}
- OPR_EXECUTOR_STORAGE_LINODE_ACCESS_KEY=$_APP_STORAGE_LINODE_ACCESS_KEY - OPR_EXECUTOR_STORAGE_LINODE_ACCESS_KEY=${_APP_STORAGE_LINODE_ACCESS_KEY}
- OPR_EXECUTOR_STORAGE_LINODE_SECRET=$_APP_STORAGE_LINODE_SECRET - OPR_EXECUTOR_STORAGE_LINODE_SECRET=${_APP_STORAGE_LINODE_SECRET}
- OPR_EXECUTOR_STORAGE_LINODE_REGION=$_APP_STORAGE_LINODE_REGION - OPR_EXECUTOR_STORAGE_LINODE_REGION=${_APP_STORAGE_LINODE_REGION}
- OPR_EXECUTOR_STORAGE_LINODE_BUCKET=$_APP_STORAGE_LINODE_BUCKET - OPR_EXECUTOR_STORAGE_LINODE_BUCKET=${_APP_STORAGE_LINODE_BUCKET}
- OPR_EXECUTOR_STORAGE_WASABI_ACCESS_KEY=$_APP_STORAGE_WASABI_ACCESS_KEY - OPR_EXECUTOR_STORAGE_WASABI_ACCESS_KEY=${_APP_STORAGE_WASABI_ACCESS_KEY}
- OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET - OPR_EXECUTOR_STORAGE_WASABI_SECRET=${_APP_STORAGE_WASABI_SECRET}
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION - OPR_EXECUTOR_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION}
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
appwrite-mariadb: appwrite-mariadb:
image: mariadb:10.11 image: mariadb:10.11
@@ -655,10 +655,10 @@ services:
volumes: volumes:
- appwrite-mariadb:/var/lib/mysql:rw - appwrite-mariadb:/var/lib/mysql:rw
environment: environment:
- MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} - MYSQL_ROOT_PASSWORD=$SERVICE_PASSWORD_MARIADBROOT
- MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_DATABASE=${_APP_DB_SCHEMA:-appwrite}
- MYSQL_USER=${_APP_DB_USER} - MYSQL_USER=$SERVICE_USER_MARIADB
- MYSQL_PASSWORD=${_APP_DB_PASS} - MYSQL_PASSWORD=$SERVICE_PASSWORD_MARIADB
command: 'mysqld --innodb-flush-method=fsync' command: 'mysqld --innodb-flush-method=fsync'
appwrite-redis: appwrite-redis:
image: redis:7.2.4-alpine image: redis:7.2.4-alpine

View File

@@ -5,41 +5,17 @@
# port: 9000 # port: 9000
services: services:
postgresql:
image: docker.io/library/postgres:12-alpine
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 2s
timeout: 10s
retries: 15
volumes:
- authentik-db:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- POSTGRES_USER=${SERVICE_USER_POSTGRESQL}
- POSTGRES_DB=authentik
redis:
image: docker.io/library/redis:alpine
command: --save 60 1 --loglevel warning
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 2s
timeout: 10s
retries: 15
volumes:
- redis:/data
authentik-server: authentik-server:
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2024.2.2} image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2024.2.2}
restart: unless-stopped restart: unless-stopped
command: server command: server
environment: environment:
- SERVICE_FQDN_AUTHENTIKSERVER_9000 - SERVICE_FQDN_AUTHENTIKSERVER_9000
- AUTHENTIK_REDIS__HOST=redis - AUTHENTIK_REDIS__HOST=${REDIS_HOST:-redis}
- AUTHENTIK_POSTGRESQL__HOST=postgresql - AUTHENTIK_POSTGRESQL__HOST=${POSTGRES_HOST:-postgresql}
- AUTHENTIK_POSTGRESQL__USER=${SERVICE_USER_POSTGRESQL} - AUTHENTIK_POSTGRESQL__USER=${SERVICE_USER_POSTGRESQL}
- AUTHENTIK_POSTGRESQL__NAME=authentik - AUTHENTIK_POSTGRESQL__NAME=${POSTGRES_DB:-authentik}
- AUTHENTIK_POSTGRESQL__PASSWORD=${SERVICE_PASSWORD_POSTGRESQL} - AUTHENTIK_POSTGRESQL__PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- AUTHENTIK_SECRET_KEY=${SERVICE_PASSWORD_64_AUTHENTIKSERVER} - AUTHENTIK_SECRET_KEY=${SERVICE_PASSWORD_64_AUTHENTIKSERVER}
- AUTHENTIK_ERROR_REPORTING__ENABLED=${AUTHENTIK_ERROR_REPORTING__ENABLED:-true} - AUTHENTIK_ERROR_REPORTING__ENABLED=${AUTHENTIK_ERROR_REPORTING__ENABLED:-true}
@@ -64,10 +40,10 @@ services:
restart: unless-stopped restart: unless-stopped
command: worker command: worker
environment: environment:
- AUTHENTIK_REDIS__HOST=redis - AUTHENTIK_REDIS__HOST=${REDIS_HOST:-redis}
- AUTHENTIK_POSTGRESQL__HOST=postgresql - AUTHENTIK_POSTGRESQL__HOST=${POSTGRES_HOST:-postgresql}
- AUTHENTIK_POSTGRESQL__USER=${SERVICE_USER_POSTGRESQL} - AUTHENTIK_POSTGRESQL__USER=${SERVICE_USER_POSTGRESQL}
- AUTHENTIK_POSTGRESQL__NAME=authentik - AUTHENTIK_POSTGRESQL__NAME=${POSTGRES_DB:-authentik}
- AUTHENTIK_POSTGRESQL__PASSWORD=${SERVICE_PASSWORD_POSTGRESQL} - AUTHENTIK_POSTGRESQL__PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- AUTHENTIK_SECRET_KEY=${SERVICE_PASSWORD_64_AUTHENTIKSERVER} - AUTHENTIK_SECRET_KEY=${SERVICE_PASSWORD_64_AUTHENTIKSERVER}
- AUTHENTIK_ERROR_REPORTING__ENABLED=${AUTHENTIK_ERROR_REPORTING__ENABLED} - AUTHENTIK_ERROR_REPORTING__ENABLED=${AUTHENTIK_ERROR_REPORTING__ENABLED}
@@ -96,3 +72,28 @@ services:
condition: service_healthy condition: service_healthy
redis: redis:
condition: service_healthy condition: service_healthy
postgresql:
image: docker.io/library/postgres:12-alpine
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 2s
timeout: 10s
retries: 15
volumes:
- authentik-db:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- POSTGRES_USER=${SERVICE_USER_POSTGRESQL}
- POSTGRES_DB=${POSTGRES_DB:-authentik}
redis:
image: docker.io/library/redis:alpine
command: --save 60 1 --loglevel warning
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 2s
timeout: 10s
retries: 15
volumes:
- redis:/data

View File

@@ -8,8 +8,8 @@ services:
browserless: browserless:
image: ghcr.io/browserless/chromium image: ghcr.io/browserless/chromium
environment: environment:
- TOKEN=$SERVICE_BASE64_BROWSERLESS_TOKEN
- SERVICE_FQDN_BROWSERLESS_3000 - SERVICE_FQDN_BROWSERLESS_3000
- TOKEN=$SERVICE_BASE64_BROWSERLESS_TOKEN
expose: expose:
- 3000 - 3000
healthcheck: healthcheck:

View File

@@ -16,7 +16,7 @@ services:
environment: environment:
- SERVICE_FQDN_PLUNK_3000 - SERVICE_FQDN_PLUNK_3000
- REDIS_URL=redis://redis:6379 - REDIS_URL=redis://redis:6379
- DATABASE_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@postgresql/docmost?schema=public - DATABASE_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@postgresql/plunk?schema=public
- JWT_SECRET=${SERVICE_PASSWORD_JWT_SECRET} - JWT_SECRET=${SERVICE_PASSWORD_JWT_SECRET}
- AWS_REGION=${AWS_REGION} - AWS_REGION=${AWS_REGION}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}

File diff suppressed because one or more lines are too long

View File

@@ -88,114 +88,64 @@ networks:
]); ]);
$this->serviceYaml = ' $this->serviceYaml = '
services: services:
chatwoot: activepieces:
image: chatwoot/chatwoot:latest image: "ghcr.io/activepieces/activepieces:latest"
depends_on:
- postgres
- redis
environment: environment:
- SERVICE_FQDN_CHATWOOT_3000 - SERVICE_FQDN_ACTIVEPIECES
- SECRET_KEY_BASE=$SERVICE_PASSWORD_CHATWOOT - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
- FRONTEND_URL=${SERVICE_FQDN_CHATWOOT} - AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
- DEFAULT_LOCALE=${CHATWOOT_DEFAULT_LOCALE} - AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js
- FORCE_SSL=false - AP_ENVIRONMENT=prod
- ENABLE_ACCOUNT_SIGNUP=false - AP_EXECUTION_MODE=${AP_EXECUTION_MODE}
- REDIS_URL=redis://default@redis:6379 - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES
- REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS - AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT
- REDIS_OPENSSL_VERIFY_MODE=none - AP_POSTGRES_DATABASE=activepieces
- POSTGRES_DATABASE=chatwoot - AP_POSTGRES_HOST=postgres
- POSTGRES_HOST=postgres - AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- POSTGRES_USERNAME=$SERVICE_USER_POSTGRES_USER - AP_POSTGRES_PORT=5432
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES
- RAILS_MAX_THREADS=5 - AP_REDIS_HOST=redis
- NODE_ENV=production - AP_REDIS_PORT=6379
- RAILS_ENV=production - AP_SANDBOX_RUN_TIME_SECONDS=600
- INSTALLATION_ENV=docker - AP_TELEMETRY_ENABLED=true
- MAILER_SENDER_EMAIL=${CHATWOOT_MAILER_SENDER_EMAIL} - "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates"
- SMTP_ADDRESS=${CHATWOOT_SMTP_ADDRESS} - AP_TRIGGER_DEFAULT_POLL_INTERVAL=5
- SMTP_AUTHENTICATION=${CHATWOOT_SMTP_AUTHENTICATION} - AP_WEBHOOK_TIMEOUT_SECONDS=30
- SMTP_DOMAIN=${CHATWOOT_SMTP_DOMAIN} depends_on:
- SMTP_ENABLE_STARTTLS_AUTO=${CHATWOOT_SMTP_ENABLE_STARTTLS_AUTO} postgres:
- SMTP_PORT=${CHATWOOT_SMTP_PORT} condition: service_healthy
- SMTP_USERNAME=${CHATWOOT_SMTP_USERNAME} redis:
- SMTP_PASSWORD=${CHATWOOT_SMTP_PASSWORD} condition: service_started
- ACTIVE_STORAGE_SERVICE=local
entrypoint: docker/entrypoints/rails.sh
command: sh -c "bundle exec rails db:chatwoot_prepare && bundle exec rails s -p 3000 -b 0.0.0.0"
volumes:
- rails-data:/app/storage
healthcheck: healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:3000"] test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
interval: 5s interval: 5s
timeout: 20s timeout: 20s
retries: 10 retries: 10
sidekiq:
image: chatwoot/chatwoot:latest
depends_on:
- postgres
- redis
environment:
- SECRET_KEY_BASE=$SERVICE_PASSWORD_CHATWOOT
- FRONTEND_URL=${SERVICE_FQDN_CHATWOOT}
- DEFAULT_LOCALE=${CHATWOOT_DEFAULT_LOCALE}
- FORCE_SSL=false
- ENABLE_ACCOUNT_SIGNUP=false
- REDIS_URL=redis://default@redis:6379
- REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS
- REDIS_OPENSSL_VERIFY_MODE=none
- POSTGRES_DATABASE=chatwoot
- POSTGRES_HOST=postgres
- POSTGRES_USERNAME=$SERVICE_USER_POSTGRES_USER
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- RAILS_MAX_THREADS=5
- NODE_ENV=production
- RAILS_ENV=production
- INSTALLATION_ENV=docker
- MAILER_SENDER_EMAIL=${CHATWOOT_MAILER_SENDER_EMAIL}
- SMTP_ADDRESS=${CHATWOOT_SMTP_ADDRESS}
- SMTP_AUTHENTICATION=${CHATWOOT_SMTP_AUTHENTICATION}
- SMTP_DOMAIN=${CHATWOOT_SMTP_DOMAIN}
- SMTP_ENABLE_STARTTLS_AUTO=${CHATWOOT_SMTP_ENABLE_STARTTLS_AUTO}
- SMTP_PORT=${CHATWOOT_SMTP_PORT}
- SMTP_USERNAME=${CHATWOOT_SMTP_USERNAME}
- SMTP_PASSWORD=${CHATWOOT_SMTP_PASSWORD}
- ACTIVE_STORAGE_SERVICE=local
command: ["bundle", "exec", "sidekiq", "-C", "config/sidekiq.yml"]
volumes:
- sidekiq-data:/app/storage
healthcheck:
test: ["CMD-SHELL", "bundle exec rails runner \'puts Sidekiq.redis(&:info)\' > /dev/null 2>&1"]
interval: 30s
timeout: 10s
retries: 3
postgres: postgres:
image: postgres:12 image: "nginx"
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
environment: environment:
- POSTGRES_DB=chatwoot - SERVICE_FQDN_ACTIVEPIECES=/api
- POSTGRES_USER=$SERVICE_USER_POSTGRES_USER - POSTGRES_DB=activepieces
- PASSW=$AP_POSTGRES_PASSWORD
- AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
healthcheck: - POSTGRES_USER=$SERVICE_USER_POSTGRES
test: ["CMD-SHELL", "pg_isready -U $SERVICE_USER_POSTGRES_USER -d chatwoot -h 127.0.0.1"]
interval: 30s
timeout: 10s
retries: 5
redis:
image: redis:alpine
restart: always
command: ["sh", "-c", "redis-server --requirepass \"$SERVICE_PASSWORD_REDIS\""]
volumes: volumes:
- redis-data:/data - "pg-data:/var/lib/postgresql/data"
healthcheck: healthcheck:
test: ["CMD", "redis-cli", "-a", "$SERVICE_PASSWORD_REDIS", "PING"] test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 30s interval: 5s
timeout: 10s timeout: 20s
retries: 5 retries: 10
redis:
image: "redis:latest"
volumes:
- "redis_data:/data"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 20s
retries: 10
'; ';
@@ -221,6 +171,7 @@ afterEach(function () {
test('ServiceComposeParseNew', function () { test('ServiceComposeParseNew', function () {
$output = newParser($this->service); $output = newParser($this->service);
$this->service->saveComposeConfigs();
// ray('New parser'); // ray('New parser');
// ray($output->toArray()); // ray($output->toArray());
ray($this->service->environment_variables->pluck('value', 'key')->toArray()); ray($this->service->environment_variables->pluck('value', 'key')->toArray());

View File

@@ -1,13 +1,13 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.331" "version": "4.0.0-beta.335"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.332" "version": "4.0.0-beta.336"
}, },
"helper": { "helper": {
"version": "1.0.0" "version": "1.0.1"
} }
} }
} }