feat: add metabase
feat: consistent container names fix: for services, you only need to add basicauth label, others are added by coolify fix: label uuids are not randomly generated all the time fix: changing force https will change the labels
This commit is contained in:
parent
80035395ff
commit
99efa857f4
@ -133,6 +133,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
|
||||||
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||||
|
ray('New container name: ', $this->container_name);
|
||||||
|
|
||||||
savePrivateKeyToFs($this->server);
|
savePrivateKeyToFs($this->server);
|
||||||
$this->saved_outputs = collect();
|
$this->saved_outputs = collect();
|
||||||
|
|
||||||
@ -711,9 +713,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->write_deployment_configurations();
|
$this->write_deployment_configurations();
|
||||||
$this->server = $this->original_server;
|
$this->server = $this->original_server;
|
||||||
}
|
}
|
||||||
if (count($this->application->ports_mappings_array) > 0) {
|
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||||
$this->application_deployment_queue->addLogEntry("----------------------------------------");
|
$this->application_deployment_queue->addLogEntry("----------------------------------------");
|
||||||
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
if (count($this->application->ports_mappings_array) > 0) {
|
||||||
|
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
||||||
|
}
|
||||||
|
if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||||
|
$this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported.");
|
||||||
|
}
|
||||||
$this->stop_running_container(force: true);
|
$this->stop_running_container(force: true);
|
||||||
$this->start_by_compose_file();
|
$this->start_by_compose_file();
|
||||||
} else {
|
} else {
|
||||||
@ -1199,13 +1206,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// ];
|
// ];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
|
||||||
|
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||||
data_forget($docker_compose, 'services.' . $this->container_name);
|
if (count($custom_compose) > 0) {
|
||||||
|
$docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
|
||||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
}
|
||||||
if (count($custom_compose) > 0) {
|
} else {
|
||||||
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||||
|
data_forget($docker_compose, 'services.' . $this->container_name);
|
||||||
|
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||||
|
if (count($custom_compose) > 0) {
|
||||||
|
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
@ -1490,6 +1502,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $containerName >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
[executeInDocker($this->deployment_uuid, "docker rm -f $containerName >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
if ($this->application->settings->is_consistent_container_name_enabled) {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
|
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
|
||||||
$this->application_deployment_queue->update([
|
$this->application_deployment_queue->update([
|
||||||
|
@ -8,20 +8,25 @@ use Livewire\Component;
|
|||||||
class Advanced extends Component
|
class Advanced extends Component
|
||||||
{
|
{
|
||||||
public Application $application;
|
public Application $application;
|
||||||
|
public bool $is_force_https_enabled;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
||||||
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
||||||
'application.settings.is_preview_deployments_enabled' => 'boolean|required',
|
'application.settings.is_preview_deployments_enabled' => 'boolean|required',
|
||||||
'application.settings.is_auto_deploy_enabled' => 'boolean|required',
|
'application.settings.is_auto_deploy_enabled' => 'boolean|required',
|
||||||
'application.settings.is_force_https_enabled' => 'boolean|required',
|
'is_force_https_enabled' => 'boolean|required',
|
||||||
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
||||||
'application.settings.is_gpu_enabled' => 'boolean|required',
|
'application.settings.is_gpu_enabled' => 'boolean|required',
|
||||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
|
||||||
'application.settings.gpu_driver' => 'string|required',
|
'application.settings.gpu_driver' => 'string|required',
|
||||||
'application.settings.gpu_count' => 'string|required',
|
'application.settings.gpu_count' => 'string|required',
|
||||||
'application.settings.gpu_device_ids' => 'string|required',
|
'application.settings.gpu_device_ids' => 'string|required',
|
||||||
'application.settings.gpu_options' => 'string|required',
|
'application.settings.gpu_options' => 'string|required',
|
||||||
];
|
];
|
||||||
|
public function mount() {
|
||||||
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
if ($this->application->isLogDrainEnabled()) {
|
if ($this->application->isLogDrainEnabled()) {
|
||||||
@ -31,7 +36,8 @@ class Advanced extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->application->settings->is_force_https_enabled) {
|
if ($this->application->settings->is_force_https_enabled !== $this->is_force_https_enabled) {
|
||||||
|
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
||||||
$this->dispatch('resetDefaultLabels', false);
|
$this->dispatch('resetDefaultLabels', false);
|
||||||
}
|
}
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
|
@ -126,7 +126,6 @@ class General extends Component
|
|||||||
$this->application->save();
|
$this->application->save();
|
||||||
}
|
}
|
||||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||||
$this->checkLabelUpdates();
|
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
@ -184,15 +183,6 @@ class General extends Component
|
|||||||
$this->submit();
|
$this->submit();
|
||||||
$this->dispatch('build_pack_updated');
|
$this->dispatch('build_pack_updated');
|
||||||
}
|
}
|
||||||
public function checkLabelUpdates()
|
|
||||||
{
|
|
||||||
if (md5($this->application->custom_labels) !== md5(implode("|", generateLabelsApplication($this->application)))) {
|
|
||||||
$this->labelsChanged = true;
|
|
||||||
} else {
|
|
||||||
$this->labelsChanged = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWildcardDomain()
|
public function getWildcardDomain()
|
||||||
{
|
{
|
||||||
$server = data_get($this->application, 'destination.server');
|
$server = data_get($this->application, 'destination.server');
|
||||||
@ -246,7 +236,7 @@ class General extends Component
|
|||||||
if ($this->application->additional_servers->count() === 0) {
|
if ($this->application->additional_servers->count() === 0) {
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.","Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='text-white underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
|
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='text-white underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,7 +269,6 @@ class General extends Component
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->checkLabelUpdates();
|
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -470,7 +470,7 @@ class Application extends BaseModel
|
|||||||
{
|
{
|
||||||
return data_get($this, 'settings.is_log_drain_enabled', false);
|
return data_get($this, 'settings.is_log_drain_enabled', false);
|
||||||
}
|
}
|
||||||
public function isConfigurationChanged($save = false)
|
public function isConfigurationChanged(bool $save = false)
|
||||||
{
|
{
|
||||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||||
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
||||||
|
@ -123,10 +123,14 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data
|
|||||||
|
|
||||||
function generateApplicationContainerName(Application $application, $pull_request_id = 0)
|
function generateApplicationContainerName(Application $application, $pull_request_id = 0)
|
||||||
{
|
{
|
||||||
|
$consistent_container_name = $application->settings->is_consistent_container_name_enabled;
|
||||||
$now = now()->format('Hisu');
|
$now = now()->format('Hisu');
|
||||||
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||||
return $application->uuid . '-pr-' . $pull_request_id;
|
return $application->uuid . '-pr-' . $pull_request_id;
|
||||||
} else {
|
} else {
|
||||||
|
if ($consistent_container_name) {
|
||||||
|
return $application->uuid;
|
||||||
|
}
|
||||||
return $application->uuid . '-' . $now;
|
return $application->uuid . '-' . $now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,15 +213,34 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource,
|
|||||||
}
|
}
|
||||||
return $payload;
|
return $payload;
|
||||||
}
|
}
|
||||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null)
|
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null)
|
||||||
{
|
{
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels->push('traefik.enable=true');
|
$labels->push('traefik.enable=true');
|
||||||
$labels->push("traefik.http.middlewares.gzip.compress=true");
|
$labels->push("traefik.http.middlewares.gzip.compress=true");
|
||||||
$labels->push("traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https");
|
$labels->push("traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https");
|
||||||
|
|
||||||
|
$basic_auth = false;
|
||||||
|
$basic_auth_middleware = null;
|
||||||
|
|
||||||
|
if ($serviceLabels) {
|
||||||
|
$basic_auth = $serviceLabels->contains(function ($value) {
|
||||||
|
return str_contains($value, 'basicauth');
|
||||||
|
});
|
||||||
|
if ($basic_auth) {
|
||||||
|
$basic_auth_middleware = $serviceLabels
|
||||||
|
->map(function ($item) {
|
||||||
|
if (preg_match('/traefik\.http\.middlewares\.(.*?)\.basicauth\.users/', $item, $matches)) {
|
||||||
|
return $matches[1];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->filter()
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach ($domains as $loop => $domain) {
|
foreach ($domains as $loop => $domain) {
|
||||||
try {
|
try {
|
||||||
$uuid = new Cuid2(7);
|
// $uuid = new Cuid2(7);
|
||||||
$url = Url::fromString($domain);
|
$url = Url::fromString($domain);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$path = $url->getPath();
|
$path = $url->getPath();
|
||||||
@ -239,11 +262,18 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
}
|
}
|
||||||
if ($path !== '/') {
|
if ($path !== '/') {
|
||||||
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
|
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix,gzip");
|
$middlewares = "gzip,{$https_label}-stripprefix";
|
||||||
|
if ($basic_auth && $basic_auth_middleware) {
|
||||||
|
$middlewares = $middlewares . ',' . $basic_auth_middleware;
|
||||||
|
}
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
||||||
} else {
|
} else {
|
||||||
$labels->push("traefik.http.routers.{$https_label}.middlewares=gzip");
|
$middlewares = "gzip";
|
||||||
|
if ($basic_auth && $basic_auth_middleware) {
|
||||||
|
$middlewares = $middlewares . ',' . $basic_auth_middleware;
|
||||||
|
}
|
||||||
|
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$labels->push("traefik.http.routers.{$https_label}.tls=true");
|
$labels->push("traefik.http.routers.{$https_label}.tls=true");
|
||||||
$labels->push("traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt");
|
$labels->push("traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt");
|
||||||
|
|
||||||
@ -267,16 +297,23 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
}
|
}
|
||||||
if ($path !== '/') {
|
if ($path !== '/') {
|
||||||
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
|
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix,gzip");
|
$middlewares = "gzip,{$http_label}-stripprefix";
|
||||||
|
if ($basic_auth && $basic_auth_middleware) {
|
||||||
|
$middlewares = $middlewares . ',' . $basic_auth_middleware;
|
||||||
|
}
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
||||||
} else {
|
} else {
|
||||||
$labels->push("traefik.http.routers.{$http_label}.middlewares=gzip");
|
$middlewares = "gzip";
|
||||||
|
if ($basic_auth && $basic_auth_middleware) {
|
||||||
|
$middlewares = $middlewares . ',' . $basic_auth_middleware;
|
||||||
|
}
|
||||||
|
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $labels->sort();
|
return $labels->sort();
|
||||||
}
|
}
|
||||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
||||||
|
@ -1039,7 +1039,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
if (!$isDatabase && $fqdns->count() > 0) {
|
if (!$isDatabase && $fqdns->count() > 0) {
|
||||||
if ($fqdns) {
|
if ($fqdns) {
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($resource->uuid, $fqdns, true));
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($resource->uuid, $fqdns, true, serviceLabels: $serviceLabels));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($resource->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) {
|
if ($resource->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) {
|
||||||
@ -1480,7 +1480,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
return $preview_fqdn;
|
return $preview_fqdn;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($uuid, $fqdns));
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($uuid, $fqdns,serviceLabels: $serviceLabels));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_consistent_container_name_enabled')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_consistent_container_name_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -15,7 +15,11 @@
|
|||||||
@endif
|
@endif
|
||||||
<x-forms.checkbox
|
<x-forms.checkbox
|
||||||
helper="Your application will be available only on https if your domain starts with https://..."
|
helper="Your application will be available only on https if your domain starts with https://..."
|
||||||
instantSave id="application.settings.is_force_https_enabled" label="Force Https" />
|
instantSave id="is_force_https_enabled" label="Force Https" />
|
||||||
|
<x-forms.checkbox
|
||||||
|
helper="The deployed container will have the same name ({{ $application->uuid }}). <span class='font-bold text-warning'>You will lose the rolling update feature!</span>"
|
||||||
|
instantSave id="application.settings.is_consistent_container_name_enabled"
|
||||||
|
label="Consistent Container Names" />
|
||||||
<h4>Logs</h4>
|
<h4>Logs</h4>
|
||||||
@if (!$application->settings->is_raw_compose_deployment_enabled)
|
@if (!$application->settings->is_raw_compose_deployment_enabled)
|
||||||
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
|
35
templates/compose/metabase.yaml
Normal file
35
templates/compose/metabase.yaml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# documentation: https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker
|
||||||
|
# slogan: Fast analytics with the friendly UX and integrated tooling to let your company explore data on their own.
|
||||||
|
# tags: analytics,bi,business,intelligence
|
||||||
|
|
||||||
|
services:
|
||||||
|
metabase:
|
||||||
|
image: metabase/metabase:latest
|
||||||
|
volumes:
|
||||||
|
- /dev/urandom:/dev/random:ro
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_METABASE
|
||||||
|
- MB_DB_TYPE=postgres
|
||||||
|
- MB_DB_HOST=postgresql
|
||||||
|
- MB_DB_PORT=5432
|
||||||
|
- MB_DB_DBNAME=${POSTGRESQL_DATABASE:-metabase}
|
||||||
|
- MB_DB_USER=$SERVICE_USER_POSTGRESQL
|
||||||
|
- MB_DB_PASS=$SERVICE_PASSWORD_POSTGRESQL
|
||||||
|
healthcheck:
|
||||||
|
test: curl --fail -I http://localhost:3000/api/health || exit 1
|
||||||
|
interval: 5s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
||||||
|
postgresql:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
volumes:
|
||||||
|
- metabase-postgresql-data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=${SERVICE_USER_POSTGRESQL}
|
||||||
|
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
|
||||||
|
- POSTGRES_DB=${POSTGRESQL_DATABASE:-metabase}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
@ -329,6 +329,17 @@
|
|||||||
"meilisearch"
|
"meilisearch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"metabase": {
|
||||||
|
"documentation": "https:\/\/www.metabase.com\/docs\/latest\/installation-and-operation\/running-metabase-on-docker",
|
||||||
|
"slogan": "Fast analytics with the friendly UX and integrated tooling to let your company explore data on their own.",
|
||||||
|
"compose": "c2VydmljZXM6CiAgbWV0YWJhc2U6CiAgICBpbWFnZTogJ21ldGFiYXNlL21ldGFiYXNlOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJy9kZXYvdXJhbmRvbTovZGV2L3JhbmRvbTpybycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRVRBQkFTRQogICAgICAtIE1CX0RCX1RZUEU9cG9zdGdyZXMKICAgICAgLSBNQl9EQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBNQl9EQl9QT1JUPTU0MzIKICAgICAgLSAnTUJfREJfREJOQU1FPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotbWV0YWJhc2V9JwogICAgICAtIE1CX0RCX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMCiAgICAgIC0gTUJfREJfUEFTUz0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogJ2N1cmwgLS1mYWlsIC1JIGh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hcGkvaGVhbHRoIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21ldGFiYXNlLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LW1ldGFiYXNlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
|
||||||
|
"tags": [
|
||||||
|
"analytics",
|
||||||
|
"bi",
|
||||||
|
"business",
|
||||||
|
"intelligence"
|
||||||
|
]
|
||||||
|
},
|
||||||
"metube": {
|
"metube": {
|
||||||
"documentation": "https:\/\/github.com\/alexta69\/metube",
|
"documentation": "https:\/\/github.com\/alexta69\/metube",
|
||||||
"slogan": "A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.",
|
"slogan": "A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user