From b45f60e374e0079b02f614557123ba8ee493411a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 23 Apr 2026 09:09:38 +0200 Subject: [PATCH 001/214] changelog: images updates, 2026W17 --- content/changelog/2026/04-21-images-update.md | 40 +++++++++++++++++++ data/runtime_versions.yml | 4 +- 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 content/changelog/2026/04-21-images-update.md diff --git a/content/changelog/2026/04-21-images-update.md b/content/changelog/2026/04-21-images-update.md new file mode 100644 index 000000000..972f24d7a --- /dev/null +++ b/content/changelog/2026/04-21-images-update.md @@ -0,0 +1,40 @@ +--- +title: "Images update: Node.js 24.15, Clever Tools 4.8, nginx 1.30, FrankenPHP 1.12.2" +description: All runtimes updated except PHP, with a new Clever Tools release and many tool updates +date: 2026-04-21 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images, except PHP. Deployment is in progress for all our users. + +* **Common:** + * Linux kernel 6.19.13 + * Chromium 147.0.7727.101 + * Clever Tools 4.8.0 + * htop 3.5.0 + * nano 9.0 + * nginx 1.30.0 + * OpenSSH 10.3p1 + * OpenSSL 3.5.6 + * SQLite 3.53.0 +* **FrankenPHP:** + * Update to 1.12.2 (with `CC_PHP_VERSION=8.5`) + * Symfony CLI 5.17.1 +* **Go:** + * Update to 1.26.2 +* **Node.js & Bun:** + * Update to 24.15.0 (npm 11.12.1) + * Bun 1.3.12 +* **Python:** + * Update to 3.13.13 + * Update to 3.14.4 + * uv 0.11.7 +* **Ruby:** + * Update to 3.2.11 diff --git a/data/runtime_versions.yml b/data/runtime_versions.yml index b41e1bc64..a5521476d 100644 --- a/data/runtime_versions.yml +++ b/data/runtime_versions.yml @@ -1,7 +1,7 @@ bun: eol_source: https://github.com/oven-sh/bun/releases default: - - 1.3.11 + - 1.3.12 caddy: eol_source: https://github.com/caddyserver/caddy/releases @@ -56,7 +56,7 @@ java: node: eol_source: https://nodejs.org/en/about/releases/ default: - - 24.14.1 (npm 11.11.0) + - 24.15.0 (npm 11.12.1) php: eol_source: https://www.php.net/supported-versions.php From 0663bdec676919c78ce5e3300092729de875ddc9 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 24 Apr 2026 16:09:08 +0200 Subject: [PATCH 002/214] changelog: Kubernetes 1.36 --- .../changelog/2026/04-24-kubernetes-1.36.md | 21 +++++++++++++++++++ content/doc/kubernetes/_index.md | 8 +++---- 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 content/changelog/2026/04-24-kubernetes-1.36.md diff --git a/content/changelog/2026/04-24-kubernetes-1.36.md b/content/changelog/2026/04-24-kubernetes-1.36.md new file mode 100644 index 000000000..7ffe44044 --- /dev/null +++ b/content/changelog/2026/04-24-kubernetes-1.36.md @@ -0,0 +1,21 @@ +--- +title: Kubernetes 1.36 is available +description: Fine-grained kubelet authorization, faster SELinux volume labelling, workload aware scheduling and removal of the gitRepo volume plugin +date: 2026-04-24 +tags: + - kubernetes + - release +authors: + - name: Gilles Biannic + link: https://github.com/GillesBIANNIC + image: https://github.com/GillesBIANNIC.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We added support for [Kubernetes 1.36.0 "Haru"](https://github.com/kubernetes/kubernetes/releases/tag/v1.36.0), which ships multiple enhancements. Fine-grained kubelet API authorization and faster SELinux labelling of volumes reach GA, while Workload Aware Scheduling and DRA device taints and tolerations move to beta. The long-deprecated `gitRepo` volume plugin is permanently removed, and `Service.spec.externalIPs` enters deprecation ahead of its removal in v1.43. Kubernetes 1.36.0 can be selected for new deployments; v1.35 remains the default for now. Migration of existing clusters will be available soon. + +- [Learn more about Kubernetes 1.36](https://kubernetes.io/blog/2026/04/22/kubernetes-v1-36-release/) +- [Learn more about Kubernetes on Clever Cloud](/doc/kubernetes/) diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index d1f7fe861..5f0289a01 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -76,12 +76,10 @@ clever k8s list --org Clever Cloud follows [the official Kubernetes version support policy](https://kubernetes.io/releases/), which maintains support for the most recent three minor versions (n-2). At any given time, the Kubernetes project maintains release branches for the latest three minor releases. -For example, if the latest release, used as default, is v1.35: - -* v1.35 (current) +* v1.36 (supported) +* v1.35 (default) * v1.34 (supported) -* v1.33 (supported) -* v1.32 (unsupported) +* v1.33 (unsupported) Each supported Kubernetes minor version typically receives patch releases for approximately 12 months after its initial release. It's a good practice to maintain your clusters on a supported version to benefit from the latest security patches, bug fixes, and features. For clusters running unsupported versions, Clever Cloud reserves the right to initiate automatic upgrades to ensure platform security and stability. From 5acac2992215f446e3a37cdeda98779ea436016d Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 24 Apr 2026 18:24:02 +0200 Subject: [PATCH 003/214] chore: update Hextra theme to 0.12.2 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f9f6dd026..990a24e38 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/CleverCloud/documentation go 1.26 -require github.com/imfing/hextra v0.12.1 // indirect +require github.com/imfing/hextra v0.12.2 // indirect diff --git a/go.sum b/go.sum index 34448dbd0..72fad666f 100644 --- a/go.sum +++ b/go.sum @@ -4,3 +4,5 @@ github.com/imfing/hextra v0.12.0 h1:f6y35hW/WDJEcx9S0dOmbICOBxYE0PmP6IJFsTUgVyY= github.com/imfing/hextra v0.12.0/go.mod h1:YAv8XRNSmcqjieFwI7fVQK1AoY2Do+45DO9HGqxSGu4= github.com/imfing/hextra v0.12.1 h1:3t1n0bmJbDzSTVfht93UDcfF1BXMRjeFojA071ri2l8= github.com/imfing/hextra v0.12.1/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= +github.com/imfing/hextra v0.12.2 h1:qa+cHQ1LC/7ys9EhRNnHrRBAHu83Tm8rhh1oWO2a7cc= +github.com/imfing/hextra v0.12.2/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= From cc72de75b2dc81029b79d6fb22dce20bf95c9912 Mon Sep 17 00:00:00 2001 From: Gilles BIANNIC Date: Tue, 21 Apr 2026 10:11:47 +0200 Subject: [PATCH 004/214] kubernetes: update flavors and versions --- content/doc/kubernetes/_index.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index 5f0289a01..0aa6c2f1f 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -76,8 +76,10 @@ clever k8s list --org Clever Cloud follows [the official Kubernetes version support policy](https://kubernetes.io/releases/), which maintains support for the most recent three minor versions (n-2). At any given time, the Kubernetes project maintains release branches for the latest three minor releases. +The most recent Kubernetes minor version is v1.36 and CKE default version is currently set to the v1.35: + * v1.36 (supported) -* v1.35 (default) +* v1.35 (supported) #default * v1.34 (supported) * v1.33 (unsupported) @@ -127,6 +129,21 @@ A node group is a collection of Kubernetes nodes that function as the compute re Once your cluster deployed and configured you can create a node group using `kubectl` from a YAML file that defines the `NodeGroup` resource. +### Available flavors + +Each node in a node group uses a specific flavor that determines its compute resources: + +| Flavor | vCPU | RAM | +|--------|------|--------| +| 2XS | 4 | 4 GB | +| XS | 6 | 8 GB | +| S | 8 | 12 GB | +| M | 10 | 16 GB | +| L | 12 | 24 GB | +| XL | 16 | 32 GB | + +The flavor is immutable after node group creation. To change the flavor, you must create a new node group. + For example, create a file named `example-nodegroup.yaml` with the following content: ```yaml{filename="example-nodegroup.yaml"} @@ -135,7 +152,7 @@ kind: NodeGroup metadata: name: example-nodegroup spec: - flavor: M + flavor: L nodeCount: 2 ``` @@ -152,8 +169,8 @@ You can list the node groups of your cluster using `kubectl`: ```bash kubectl get nodegroups -NAME DESIREDNODECOUNT CURRENTNODECOUNT FLAVOR AGE -default 2 2 M 2m +NAME DESIREDNODECOUNT CURRENTNODECOUNT FLAVOR STATUS AGE +example-nodegroup 2 2 L Synced 2m ``` The `DESIREDNODECOUNT` is the number of nodes that you asked for, the `CURRENTNODECOUNT` is the number of nodes currently in the node group. When creating a node group, the `CURRENTNODECOUNT` is `0` and increases until it reaches the `DESIREDNODECOUNT`. @@ -163,9 +180,9 @@ You can also list the nodes of your cluster using `kubectl`: ```bash kubectl get nodes -NAME STATUS ROLES AGE VERSION -default-node0 Ready 6d17h v1.34.1 -default-node1 Ready 3d18h v1.34.1 +NAME STATUS ROLES AGE VERSION +example-nodegroup-node0 Ready 5d22h v1.35.4 +example-nodegroup-node1 Ready 3d18h v1.35.4 ``` ## Scaling a node group From d96ac52d476f02b761c9cf961a00965212446740 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 27 Apr 2026 13:33:01 +0200 Subject: [PATCH 005/214] kubernetes: more details, public beta Co-Authored-By: Gilles BIANNIC --- .../changelog/2026/04-22-kubernetes-1.36.md | 21 + content/doc/kubernetes/_index.md | 448 ++++++++++++++---- data/kubernetes_versions.yml | 12 + layouts/shortcodes/kubernetes_version.html | 3 + 4 files changed, 398 insertions(+), 86 deletions(-) create mode 100644 content/changelog/2026/04-22-kubernetes-1.36.md create mode 100644 data/kubernetes_versions.yml create mode 100644 layouts/shortcodes/kubernetes_version.html diff --git a/content/changelog/2026/04-22-kubernetes-1.36.md b/content/changelog/2026/04-22-kubernetes-1.36.md new file mode 100644 index 000000000..89a5a793f --- /dev/null +++ b/content/changelog/2026/04-22-kubernetes-1.36.md @@ -0,0 +1,21 @@ +--- +title: Kubernetes 1.36 is available +description: Faster SELinux volume labeling, external ServiceAccount token signing, device taints for Dynamic Resource Allocation, and gitRepo volume driver removal +date: 2026-04-22 +tags: + - kubernetes + - release +authors: + - name: Gilles Biannic + link: https://github.com/GillesBIANNIC + image: https://github.com/GillesBIANNIC.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Kubernetes [release 1.36.0](https://github.com/kubernetes/kubernetes/releases/tag/v1.36.0) is available on Clever Kubernetes Engine. Headline changes include faster SELinux volume labeling promoted to GA, external signing of ServiceAccount tokens through cloud KMS or HSMs, and Dynamic Resource Allocation gaining taints and tolerations for physical devices. The `gitRepo` volume driver — deprecated since v1.11 — has been permanently removed; migrate to init containers or git-sync tools. Select 1.36 explicitly at creation with `--cluster-version 1.36`; the default for new clusters remains 1.35. Migration of existing clusters will be available soon. + +- [Learn more about Kubernetes 1.36](https://kubernetes.io/blog/2026/03/30/kubernetes-v1-36-sneak-peek/) +- [Learn more about Kubernetes on Clever Cloud](/doc/kubernetes/) diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index 0aa6c2f1f..4563066e8 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -3,7 +3,7 @@ type: docs weight: 4 linkTitle: Kubernetes title: Clever Kubernetes Engine (CKE) -description: Create and manage Kubernetes clusters using Clever Cloud Kubernetes Engine with Materia etcd +description: Create and manage Kubernetes clusters on Clever Cloud, with managed control plane, Materia etcd and vanilla Kubernetes experience keywords: - kubernetes - cke @@ -11,10 +11,18 @@ keywords: - etcd - k8s - cluster +- topology +- all-in-one +- dedicated compute +- distributed +- node group +- autoscaling - csi - persistent storage - kubeconfig -- load balancers +- load balancer +- pricing +- quota --- Clever Kubernetes Engine (CKE) allows you to create and manage Kubernetes clusters with ease on Clever Cloud infrastructure. It uses Materia etcd, our implementation of etcd built on top of FoundationDB, as the backing store for your cluster's state. It ensures reliability at scale. @@ -23,179 +31,369 @@ Our approach remains the same as with our other products: our Kubernetes offer i We operate the Kubernetes control plane for you: upgrades, availability, and patching are our responsibility. You manage your own node pools — scaling them up or down manually as needed — while we ensure the control plane remains stable. Access is straightforward: we provide a kubeconfig file so you can use `kubectl` or any other Kubernetes compatible tool with the same workflow you already know. -> [!NOTE] Clever Kubernetes is in private access -> Ask for activation to your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) +> [!NOTE] Clever Kubernetes is in public beta +> Activate it from your [Console Labs](https://console.clever-cloud.com/users/me/feature-list) or via the Clever Tools feature flag (`clever features enable k8s`). To raise your default quota or request test credits, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). -## Prerequisites +## Why Clever Kubernetes Engine + +### Fully managed control plane on sovereign French infrastructure + +Patching, Kubernetes upgrades, certificate rotation and control plane availability are handled by Clever Cloud, on sovereign infrastructure operated end-to-end by our team in France — no foreign hyperscaler in the loop. Every control plane component and every worker runs on a **dedicated VM** — nothing is shared between clusters or between tenants. Your responsibility stops at the workloads and the node groups that host them — the control plane is just there, healthy. Clusters come with vanilla Kubernetes: you keep the classic `kubectl` workflow and you can leave the platform without rewriting a single manifest. + +### Native auto-healing + +When a control plane VM or a worker fails its health checks, the platform provisions a replacement, waits for it to become healthy, then removes the failed VM. The mechanism works with any replication factor — including `rf = 1`. No manual intervention, no `kubectl drain` to script. + +```mermaid +sequenceDiagram + participant HC as Health checks + participant Platform as Clever Cloud + participant Old as Failed VM + participant New as Replacement VM + HC->>Platform: VM unhealthy + Platform->>New: Provision replacement + New-->>Platform: Healthy + Platform->>Old: Drain workloads + Platform->>Old: Delete VM +``` + +### On-demand autoscaling + +The cluster autoscaler is opt-in, per node group. Leave it disabled for stable workloads and predictable billing, or enable it when your workloads need elasticity (`--autoscaling --min N --max N`). The autoscaler reacts to node-level resource pressure (CPU and memory load on the worker VMs) and resizes the node group within the bounds you define, up or down. See [Manage node groups](#manage-node-groups) for commands. + +### 3 datacenters in Paris and dedicated load balancer IPs + +The platform is distributed across 3 datacenters in Paris, with multi-site replication built in. Pick a control plane replication factor of `3` or more in `DEDICATED_COMPUTE` or `DISTRIBUTED`, and the VMs are spread across the three sites — a single datacenter loss does not take your control plane down. Each `LoadBalancer` Service provisions an L4 load balancer (TCP and UDP) with its own dedicated configuration and **two public IP addresses reserved for your service**. The underlying load balancer fleet is shared across customers, but every Service gets its own logical entry point and its own IPs. + +### Cilium and eBPF networking + +The CNI is Cilium, running on top of eBPF. You get fast pod-to-pod networking, native NetworkPolicies, observability hooks and the ability to plug in extra Cilium features (Hubble, mTLS, etc.) without swapping the underlying CNI. When a cluster spans multiple VMs, the underlying inter-VM transport is a [Network Group](/doc/develop/network-groups/) — a private WireGuard-based mesh that connects the control plane and worker VMs. + +### Materia etcd, made in France on FoundationDB + +The cluster state store is Materia etcd, our **serverless** implementation of the etcd API built on top of FoundationDB. Designed and developed in France by Clever Cloud engineers, Materia etcd brings FoundationDB's track record at scale to thousands of clusters without the operational burden of running, sizing or backing up etcd yourself — no etcd VM to provision, no quorum to manage. + +### Simple deployment and version management + +Create a cluster in one command, follow its rollout with `--watch`, upgrade Kubernetes with `clever k8s version update`, audit deployment events with `clever k8s activity`. The full cluster and node group lifecycle is exposed in the API and in [Clever Tools 4.9+](/doc/cli/kubernetes/) (Console support coming soon). Persistent storage is available as an opt-in through a CSI driver backed by Ceph; pods consume it via standard `PersistentVolumeClaim` resources. + +A control plane replication factor greater than `1` smooths upgrades and incident windows: VMs are rotated one at a time, so the apiserver stays reachable while the platform replaces the others. The same applies to multi-node node groups during rolling upgrades — your workloads keep running on the remaining nodes while one is being replaced. + +### Integrated with the Clever Cloud platform + +Provision PostgreSQL, Pulsar, Materia KV or any other Clever Cloud add-on directly from inside your cluster via the [Clever Kubernetes Operator](#clever-kubernetes-operator) — managed services declared as custom resources, reconciled alongside your workloads. + +## Cluster topologies -To use Clever Kubernetes you'll need : -- An authorized access for your organisation -- Clever Tools 4.3.0 or later installed -- kubectl installed +A Kubernetes cluster on Clever Cloud is made of a control plane and one or more node groups. The control plane runs the Kubernetes components (`apiserver`, `controller-manager` and `scheduler`) and the Clever Cloud operators (`cloud-controller-manager` and `node-group-operator`). Node groups are pools of worker virtual machines where your workloads are scheduled by the Kubernetes `kubelet` component. -To check if you have access to Clever Kubernetes for your organisation, run the following command in your terminal: +You choose how the control plane is distributed at creation time. The topology decides the placement of the Kubernetes components, the available flavors, whether workloads share the same VMs as the control plane, and ultimately what gets billed. Three layouts are available: + +|Topology|Customers|VMs you pay for|Flavors|Default node group included?|Typical use case| +|---|---|---|---|---|---| +|`ALL_IN_ONE`|Developers|`replicationFactor` × bundle VM (control plane + integrated worker node). Additional node groups billed separately|`S`, `M`, `L`, `XL`|No node group, but each bundle VM is also a worker node — additional node groups optional|Development, testing, small single-team clusters| +|`DEDICATED_COMPUTE`|Business|`replicationFactor` × control plane VM **plus** every worker VM in your node groups|`XS`, `S`, `M`, `L`, `XL`|No — provision a node group at creation with `--nodegroup` or later|Production clusters where control plane and workloads stay isolated| +|`DISTRIBUTED`|Enterprise|For each of 5 components: `replicationFactor` × component VM **plus** every worker VM in your node groups|`2XS`, `XS`, `S`, `M`, `L`, `XL`|No — provision a node group at creation or later|Demanding production clusters needing fine-grained HA per control plane component| + +`ALL_IN_ONE` is the default when no topology is specified at cluster creation through Clever Tools or the Console. A replication factor (`1` to `5`) applies to the control plane VMs: the higher the factor, the more resilient the control plane and, in `DEDICATED_COMPUTE` or `DISTRIBUTED`, the more sites it is spread across (up to three Parisian datacenters). In `DISTRIBUTED`, each of the five components has its own flavor and its own replication factor, letting you scale a busy `apiserver` higher than a quieter `scheduler`. + +In `ALL_IN_ONE`, every VM in the bundle is also a worker node — the bundle covers it at no extra cost. You can still attach node groups to an `ALL_IN_ONE` cluster, and those are billed at the node group rate. In `DEDICATED_COMPUTE` and `DISTRIBUTED`, the control plane VMs are dedicated to control plane workloads and your pods land on **separate worker VMs** that you provision in node groups, billed at the node group rate. See [Pricing](#pricing) for the breakdown. + +Whenever a cluster spans multiple VMs — additional node groups on `ALL_IN_ONE`, the control plane and workers in `DEDICATED_COMPUTE`, the five components and workers in `DISTRIBUTED` — the VMs talk to each other over a [Network Group](/doc/develop/network-groups/), a private WireGuard-based mesh provisioned automatically with the cluster. Inter-component traffic stays on the private network. + +**Developers (`ALL_IN_ONE`)** — one VM hosts the five control plane components plus an integrated worker node, replicated `replicationFactor` times. Additional node groups (optional) can be attached to scale compute beyond the bundle and are billed at the [node group rate](#node-groups-workers). + +```mermaid +flowchart LR + AIO["ALL_IN_ONE bundle VM (× replicationFactor)
━━━━━━━━━━━━━━━━━━━━━━
All components on the same VM

Control plane
apiserver · controller-manager · scheduler
cloud-controller-manager · node-group-operator

Integrated worker node
kubelet ← your pods"] + AIOetcd[("Materia etcd cluster")] + AIOng1["Node group 1 (optional)
kubelet ← your pods"] + AIOng2["Node group 2 (optional)
kubelet ← your pods"] + AIOng3["Node group 3 (optional)
kubelet ← your pods"] + AIO <--> AIOetcd + AIO -.-> AIOng1 + AIO -.-> AIOng2 + AIO -.-> AIOng3 + classDef optional stroke-dasharray: 5 5 + class AIOng1,AIOng2,AIOng3 optional +``` + +**Business (`DEDICATED_COMPUTE`)** — one VM dedicated to the bundled control plane, separate worker VMs in node groups. + +```mermaid +flowchart LR + DCcp["Control plane VM (× replicationFactor)
━━━━━━━━━━━━━━━━━━━━━━
All control plane components on the same VM

apiserver · controller-manager · scheduler
cloud-controller-manager · node-group-operator"] + DCetcd[("Materia etcd cluster")] + DCng1["Node group 1
kubelet ← your pods"] + DCng2["Node group 2
kubelet ← your pods"] + DCng3["Node group 3
kubelet ← your pods"] + DCcp <--> DCetcd + DCcp --> DCng1 + DCcp --> DCng2 + DCcp --> DCng3 +``` + +**Enterprise (`DISTRIBUTED`)** — one VM per control plane component (each with its own flavor and replication factor), separate worker VMs in node groups. + +```mermaid +flowchart LR + Dcm["controller-manager VM
(× replicationFactor)"] + Dsched["scheduler VM
(× replicationFactor)"] + Dccm["cloud-controller-manager VM
(× replicationFactor)"] + Dngo["node-group-operator VM
(× replicationFactor)"] + Dapi["apiserver VM
(× replicationFactor)"] + Detcd[("Materia etcd cluster")] + Dng1["Node group 1
kubelet ← your pods"] + Dng2["Node group 2
kubelet ← your pods"] + Dng3["Node group 3
kubelet ← your pods"] + Dcm --> Dapi + Dsched --> Dapi + Dccm --> Dapi + Dngo --> Dapi + Dapi <--> Detcd + Dapi --> Dng1 + Dapi --> Dng2 + Dapi --> Dng3 +``` + +Topology and replication factor are immutable after creation. Flavor is bound to the topology — changing it means recreating the cluster. + +## Prerequisites + +Clever Kubernetes is activated per user. Enable it either from your [Console Labs](https://console.clever-cloud.com/users/me/feature-list) or with the Clever Tools feature flag: ```bash clever features enable k8s -clever k8s list --org ``` -If you get an error or if you miss anything, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). +You also need: -- [kubectl installation guide](https://kubernetes.io/docs/tasks/tools/#kubectl) -- [Learn more about Clever Tools k8s command](/doc/cli/kubernetes/) +- [Clever Tools](/doc/cli/) 4.9 or later installed (recommended for the full Kubernetes command set) +- `kubectl` installed ([installation guide](https://kubernetes.io/docs/tasks/tools/#kubectl)) + +Verify your setup by listing the clusters of your organisation: + +```bash +clever k8s list --org +``` ->[!NOTE] Kubernetes clusters quotas -> During the private access phase, each organization can deploy a limited number of Kubernetes clusters.\ -> If you need more clusters, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). +- [Learn more about Clever Tools k8s command](/doc/cli/kubernetes/) ## Create a Kubernetes cluster -To create a Kubernetes cluster, use the following command: +The fastest way to create a cluster is to provide only a name. The platform picks `ALL_IN_ONE` as the default topology, the minimum flavor available for it (`S`) and a replication factor of `1`: ```bash clever k8s create clusterName --org ``` -Cluster is immediately created and starts its deployment. It takes approximately 1 minute to deploy and configure all the underlying infrastructure. If you want to monitor the progress after creation, use the `--watch` option: +The cluster is created immediately and starts its deployment. It takes approximately one minute to provision the underlying infrastructure. To follow progress until the cluster reaches the `ACTIVE` state, add `--watch`: ```bash -clever k8s create clusterNameOrId --org --watch +clever k8s create clusterName --org --watch ``` -You can list all your clusters at any time using: +When you need a specific shape, combine topology, flavor and replication factor: + +```bash +clever k8s create myCluster \ + --topology DEDICATED_COMPUTE --flavor S --replication-factor 3 \ + --cluster-version 1.36 \ + --description "Production cluster" \ + --tag env:prod,team:platform \ + --autoscaling \ + --persistent-storage \ + --nodegroup M:3 +``` + +The `--nodegroup :` option provisions an initial node group named `default` at creation time, so the cluster is ready to schedule workloads as soon as it reaches `ACTIVE`. It is the typical pattern for `DEDICATED_COMPUTE` and `DISTRIBUTED`. `ALL_IN_ONE` clusters already include an integrated worker node on each bundle VM; if you still pass `--nodegroup` on an `ALL_IN_ONE` cluster, Clever Tools warns you and prompts for confirmation before adding the extra pool. + +List your clusters at any time: ```bash clever k8s list --org ``` > [!TIP] -> In [Clever Cloud Console](https://console.clever-cloud.com), you can filter Kubernetes clusters in the left menu by searching for `is:k8s`, `is:kube` or `is:kubernetes` +> In [Clever Cloud Console](https://console.clever-cloud.com), you can filter Kubernetes clusters in the left menu by searching for `is:k8s`, `is:kube` or `is:kubernetes`. ## Supported versions Clever Cloud follows [the official Kubernetes version support policy](https://kubernetes.io/releases/), which maintains support for the most recent three minor versions (n-2). At any given time, the Kubernetes project maintains release branches for the latest three minor releases. -The most recent Kubernetes minor version is v1.36 and CKE default version is currently set to the v1.35: +The current Kubernetes release on the platform is **v{{< kubernetes_version current >}}**, available with `--cluster-version {{< kubernetes_version current >}}` at creation. New clusters default to **v{{< kubernetes_version default >}}** when no version is specified. Supported versions are {{< kubernetes_version supported >}}, unsupported versions are {{< kubernetes_version unsupported >}}. -* v1.36 (supported) -* v1.35 (supported) #default -* v1.34 (supported) -* v1.33 (unsupported) +Each Kubernetes minor version receives patch releases (security fixes, bug fixes) during a support window of approximately 12 months that starts at its initial release. After that window, the version is deprecated and stops receiving patches. It's a good practice to maintain your clusters on a supported version to benefit from the latest security patches, bug fixes, and features. For clusters running unsupported versions, Clever Cloud reserves the right to initiate automatic upgrades to ensure platform security and stability. -Each supported Kubernetes minor version typically receives patch releases for approximately 12 months after its initial release. It's a good practice to maintain your clusters on a supported version to benefit from the latest security patches, bug fixes, and features. For clusters running unsupported versions, Clever Cloud reserves the right to initiate automatic upgrades to ensure platform security and stability. +## Manage node groups -## Add persistent storage (CSI) +A node group is a pool of virtual machines of the same flavor (vCPU, RAM, location) that serves as the compute resources for your cluster. Node groups simplify scaling by letting you manage similar nodes together as a unit rather than one machine at a time. A cluster can host several node groups with different flavors, which is how you mix general-purpose workloads with larger workers for dedicated jobs. + +You can manage node groups either with Clever Tools or directly from Kubernetes using the `NodeGroup` custom resource. Both paths talk to the same Clever Cloud API and produce identical results. + +### With Clever Tools -You can use Clever Cloud block storage to attach a persistent volume to a cluster through a CSI (Container Storage Interface). Once its status is `ACTIVE`, use the following command: +Create a node group, then list or inspect them: ```bash -clever k8s add-persistent-storage clusterNameOrId --org +clever k8s nodegroups create myCluster workers M:3 +clever k8s nodegroups list myCluster +clever k8s nodegroups get myCluster workers ``` -## Get the kubeconfig file +To scale, toggle autoscaling, or change metadata, use `update`: -Get the kubeconfig file to interact with your cluster: +```bash +clever k8s nodegroups update myCluster workers --count 5 +clever k8s nodegroups update myCluster workers --autoscaling --min 2 --max 10 +clever k8s nodegroups update myCluster workers --disable-autoscaling +clever k8s nodegroups update myCluster workers --description "GPU-intensive workers" +``` + +Deleting a node group drains its nodes and removes the underlying VMs: ```bash -clever k8s get-kubeconfig clusterNameOrId --org +clever k8s nodegroups delete myCluster workers ``` -You can directly save it as your local kubeconfig file: +### With kubectl + +Define a `NodeGroup` resource in a YAML file and apply it: + +```yaml{filename="example-nodegroup.yaml"} +apiVersion: api.clever-cloud.com/v1 +kind: NodeGroup +metadata: + name: example-nodegroup +spec: + flavor: M + nodeCount: 2 +``` ```bash -clever k8s get-kubeconfig clusterNameOrId --org > ~/.kube/config +kubectl create -f example-nodegroup.yaml ``` -Check everything is working by listing the nodes of your cluster (it should be empty at this point): +The node group creation process takes approximately 60 to 90 seconds to complete. Once ready, the new nodes automatically join the cluster and become available for scheduling. Inspect them with the standard Kubernetes verbs: ```bash -# With the default kubeconfig file: +kubectl get nodegroups + +NAME DESIREDNODECOUNT CURRENTNODECOUNT FLAVOR STATUS AGE +example-nodegroup 2 2 M Synced 2m + kubectl get nodes -# To target a specific kubeconfig file: -kubectl get nodes --kubeconfig=kubeconfig.yaml +NAME STATUS ROLES AGE VERSION +example-nodegroup-node0 Ready 6d17h v1.35.4 +example-nodegroup-node1 Ready 3d18h v1.35.4 ``` -## Clever Kubernetes operator +`DESIREDNODECOUNT` is the number of nodes you asked for, `CURRENTNODECOUNT` is the number of nodes currently in the node group. When creating a node group, `CURRENTNODECOUNT` starts at `0` and increases until it reaches `DESIREDNODECOUNT`. -Clever Cloud's Kubernetes clusters are designed to work seamlessly with the rest of the platform. A good example is our [open-source Kubernetes Operator](https://github.com/CleverCloud/clever-kubernetes-operator), which allows you to easily provision and use Clever Cloud resources directly from inside your cluster and combine Kubernetes workloads with the services you already trust on Clever Cloud. +To scale with `kubectl`, use the standard `scale` verb: -- [Learn more about the Clever Cloud Kubernetes Operator](/doc/kubernetes/operator/) +```bash +kubectl scale nodegroup example-nodegroup --replicas=4 +``` -## Create a node group +### Autoscaling on demand -A node group is a collection of Kubernetes nodes that function as the compute resources for your cluster. Each node group consists of virtual machines of the same flavor, meaning they have identical characteristics: vCPU, RAM and location (Paris region only for now). Node groups simplify management by letting you scale or upgrade similar nodes together as a unit rather than individually managing each node. +The cluster autoscaler is not active by default. Enable it once at cluster level (at creation with `--autoscaling`, later with `clever k8s update --autoscaling`), then set the bounds per node group: -Once your cluster deployed and configured you can create a node group using `kubectl` from a YAML file that defines the `NodeGroup` resource. +```bash +clever k8s nodegroups update myCluster workers --autoscaling --min 2 --max 10 +``` -### Available flavors +Once enabled, the autoscaler reacts to node-level resource pressure (CPU and memory load on the worker VMs) and resizes the node group within the `--min` and `--max` bounds you defined. Switch it off with `--disable-autoscaling` on the node group when you want a fixed-size pool, or at cluster level to stop the autoscaler altogether. -Each node in a node group uses a specific flavor that determines its compute resources: +## Add persistent storage (CSI) -| Flavor | vCPU | RAM | -|--------|------|--------| -| 2XS | 4 | 4 GB | -| XS | 6 | 8 GB | -| S | 8 | 12 GB | -| M | 10 | 16 GB | -| L | 12 | 24 GB | -| XL | 16 | 32 GB | +You can use Clever Cloud block storage to attach a persistent volume to a cluster through a CSI (Container Storage Interface). Enable persistent storage at creation with `--persistent-storage`, or on an existing cluster: -The flavor is immutable after node group creation. To change the flavor, you must create a new node group. +```bash +clever k8s update clusterNameOrId --persistent-storage --org +clever k8s add-persistent-storage clusterNameOrId --org +``` -For example, create a file named `example-nodegroup.yaml` with the following content: +Both commands reach the same result. Persistent storage is currently a one-way toggle: once enabled, it cannot be removed from a running cluster. Create a new cluster without `--persistent-storage` if you no longer need it. -```yaml{filename="example-nodegroup.yaml"} -apiVersion: api.clever-cloud.com/v1 -kind: NodeGroup -metadata: - name: example-nodegroup -spec: - flavor: L - nodeCount: 2 -``` +## Get the kubeconfig file -Then, apply this configuration to your cluster using `kubectl create`: +Get the kubeconfig file to interact with your cluster: ```bash -kubectl create -f example-nodegroup.yaml +clever k8s get-kubeconfig clusterNameOrId --org ``` -The node group creation process takes approximately 60 to 90 seconds to complete. Once created, the new nodes will automatically join your cluster and become available for scheduling workloads. +You can directly save it as your local kubeconfig file: + +```bash +clever k8s get-kubeconfig clusterNameOrId --org > ~/.kube/config +``` -You can list the node groups of your cluster using `kubectl`: +Check everything is working by listing the nodes of your cluster (it should be empty at this point): ```bash -kubectl get nodegroups +# With the default kubeconfig file: +kubectl get nodes -NAME DESIREDNODECOUNT CURRENTNODECOUNT FLAVOR STATUS AGE -example-nodegroup 2 2 L Synced 2m +# To target a specific kubeconfig file: +kubectl get nodes --kubeconfig=kubeconfig.yaml ``` -The `DESIREDNODECOUNT` is the number of nodes that you asked for, the `CURRENTNODECOUNT` is the number of nodes currently in the node group. When creating a node group, the `CURRENTNODECOUNT` is `0` and increases until it reaches the `DESIREDNODECOUNT`. +## Pull images from authenticated registries -You can also list the nodes of your cluster using `kubectl`: +Public registries like Docker Hub rate-limit anonymous pulls, which can fail deployments when many pods come up at the same time. Authenticate your pulls by declaring your registry credentials inside the cluster as a standard `imagePullSecrets` and referencing it from your workloads. -```bash -kubectl get nodes +Create a secret of type `kubernetes.io/dockerconfigjson` with `kubectl`: -NAME STATUS ROLES AGE VERSION -example-nodegroup-node0 Ready 5d22h v1.35.4 -example-nodegroup-node1 Ready 3d18h v1.35.4 +```bash +kubectl create secret docker-registry dockerhub-creds \ + --docker-server=https://index.docker.io/v1/ \ + --docker-username= \ + --docker-password= \ + --docker-email= ``` -## Scaling a node group +Reference the secret from your Pod spec via `imagePullSecrets`: -To scale up or down a node group, use the `kubectl scale` command. For example, to scale the `example-nodegroup` to 4 nodes, run: +```yaml{filename="deployment-private.yaml"} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app +spec: + replicas: 2 + selector: + matchLabels: + app: my-app + template: + metadata: + labels: + app: my-app + spec: + imagePullSecrets: + - name: dockerhub-creds + containers: + - name: my-app + image: myorg/my-private-image:1.0 +``` + +To avoid repeating `imagePullSecrets` in every Pod, patch the default `ServiceAccount` of your namespace so all Pods inherit the credentials: ```bash -kubectl scale nodegroup example-nodegroup --replicas=4 +kubectl patch serviceaccount default \ + -p '{"imagePullSecrets":[{"name":"dockerhub-creds"}]}' ``` +The same pattern applies to any OCI-compliant registry (GitHub Container Registry, GitLab Registry, AWS ECR, Google Artifact Registry, self-hosted Harbor, etc.) — adjust `--docker-server`, `--docker-username` and `--docker-password` accordingly. For registries that issue short-lived tokens (ECR, GAR), rotate the secret with a CronJob or an external controller. + ## Deployment with a load balancer service -Here is an example of a simple NGINX deployment with a load balancer service: +Before deploying workloads, the cluster needs worker nodes. An `ALL_IN_ONE` cluster ships with an integrated worker node on each bundle VM, so the cluster is ready to schedule pods as soon as it reaches `ACTIVE`. With `DEDICATED_COMPUTE` or `DISTRIBUTED`, provision a node group first — either at cluster creation with `--nodegroup :` (see [Create a Kubernetes cluster](#create-a-kubernetes-cluster)) or afterwards with `clever k8s nodegroups create` (see [Manage node groups](#manage-node-groups)). + +Once workers are available, here is an example of a simple NGINX deployment with a load balancer service: ```bash kubectl create deployment nginx --image=nginx:alpine --replicas=2 @@ -221,7 +419,7 @@ spec: spec: containers: - name: nginx - image: nginx:1.14.2 + image: nginx:1.30.0 ports: - containerPort: 80 --- @@ -240,3 +438,81 @@ spec: port: 80 # Port accessible from outside the cluster targetPort: 80 # Port on which the container is listening ``` + +## Quotas and limits + +Each organisation starts with **40 vCPU and 40 GB of RAM** across all its Kubernetes clusters by default. This envelope covers both the control plane VMs and the worker nodes, whatever their topology. Check your current consumption and the remaining budget at any time with `clever k8s quota`. If you need more, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). + +Each organisation also comes with **two public IP addresses**, which corresponds to a single `LoadBalancer` service across all its clusters. Additional `LoadBalancer` services are accepted by the Kubernetes API but stay in a pending state until the quota is lifted — `kubectl describe svc` typically reports error 507 in that case. + +## Pricing + +> [!NOTE] Pricing during the public beta +> Only the control plane and compute nodes are billed for now. Persistent storage (CSI) and load balancers will be added to your invoice before the platform reaches general availability. Customers will be notified ahead of the change — follow the [changelog](/changelog/) to stay informed. + +Prices are **excluding taxes**. The platform charges by the hour, per resource (vCPU and RAM): the per-VM rate below is `vCPU × vCPU_rate + RAM_GB × RAM_rate`. Monthly prices are computed using the standard 720-hours-per-month convention (30 × 24). + +The model is: **one price per topology**, charged per control plane element per replication, **plus** the [node group rate](#node-groups-workers) for every worker VM you provision in additional node groups. `ALL_IN_ONE` is special: its bundle price already covers the five control plane components **and** an integrated worker node on each bundle VM. If you attach additional node groups to an `ALL_IN_ONE` cluster, those extra workers are billed at the node group rate. + +In practice: + +- **`ALL_IN_ONE`** — `replicationFactor` × the bundle VM price (covers the five control plane components and the integrated worker node). Additional node groups, if any, are billed at the [node group rate](#node-groups-workers). +- **`DEDICATED_COMPUTE`** — `replicationFactor` × the control plane VM price, **plus** the [node group rate](#node-groups-workers) for every worker VM in your node groups. +- **`DISTRIBUTED`** — for each of the five components, `replicationFactor` × the component VM price (each component picks its own flavor and replication factor), **plus** the [node group rate](#node-groups-workers) for every worker VM in your node groups. + +### Developers (`ALL_IN_ONE`) bundle + +The bundle hosts the five control plane components and an integrated worker node on the same VMs. The price below covers everything; additional node groups attached to an `ALL_IN_ONE` cluster are billed separately at the [node group rate](#node-groups-workers). + +|Flavor|Resources|Hourly price|Monthly price| +|---|---|---|---| +|S|8 vCPU / 12 GB|0.0889 €|64.00 €| +|M|10 vCPU / 16 GB|0.1167 €|84.00 €| +|L|12 vCPU / 24 GB|0.1667 €|120.00 €| +|XL|16 vCPU / 32 GB|0.2222 €|160.00 €| + +### Business (`DEDICATED_COMPUTE`) control plane + +One VM per replication factor, dedicated to the control plane. + +|Flavor|Resources|Hourly price|Monthly price| +|---|---|---|---| +|XS|6 vCPU / 8 GB|0.0917 €|66.00 €| +|S|8 vCPU / 12 GB|0.1333 €|96.00 €| +|M|10 vCPU / 16 GB|0.1750 €|126.00 €| +|L|12 vCPU / 24 GB|0.2500 €|180.00 €| +|XL|16 vCPU / 32 GB|0.3333 €|240.00 €| + +### Enterprise (`DISTRIBUTED`) control plane + +For each of the five components (`apiserver`, `controller-manager`, `scheduler`, `cloud-controller-manager`, `node-group-operator`), pay `replicationFactor` × the per-VM price below. Each component picks its own flavor and replication factor. + +|Flavor|Resources|Hourly price|Monthly price per component| +|---|---|---|---| +|2XS|4 vCPU / 4 GB|0.0500 €|36.00 €| +|XS|6 vCPU / 8 GB|0.0917 €|66.00 €| +|S|8 vCPU / 12 GB|0.1333 €|96.00 €| +|M|10 vCPU / 16 GB|0.1750 €|126.00 €| +|L|12 vCPU / 24 GB|0.2500 €|180.00 €| +|XL|16 vCPU / 32 GB|0.3333 €|240.00 €| + +The price above is **per component VM** (one of the five) at `replicationFactor = 1`. A minimal Distributed cluster with all five components in 2XS at rf=1 therefore costs `5 × 36.00 € = 180.00 €/month` for the control plane alone, plus the [node group rate](#node-groups-workers) for your workers. + +### Node groups (workers) + +Worker VMs in node groups attached to any cluster. An `ALL_IN_ONE` cluster ships with one worker node integrated in each bundle VM (no extra rate); any **additional** node group you attach to an `ALL_IN_ONE` cluster is billed at the rate below, like for `DEDICATED_COMPUTE` and `DISTRIBUTED`. + +|Flavor|Resources|Hourly price|Monthly price| +|---|---|---|---| +|2XS|4 vCPU / 4 GB|0.0333 €|24.00 €| +|XS|6 vCPU / 8 GB|0.0611 €|44.00 €| +|S|8 vCPU / 12 GB|0.0889 €|64.00 €| +|M|10 vCPU / 16 GB|0.1167 €|84.00 €| +|L|12 vCPU / 24 GB|0.1667 €|120.00 €| +|XL|16 vCPU / 32 GB|0.2222 €|160.00 €| + +## Clever Kubernetes Operator + +Clever Cloud's Kubernetes clusters are designed to work seamlessly with the rest of the platform. The [open-source Clever Kubernetes Operator](https://github.com/CleverCloud/clever-kubernetes-operator) lets you declare PostgreSQL databases, Pulsar topics, and any other Clever Cloud add-on directly inside your cluster via custom resources, and combine these managed services with your Kubernetes workloads. + +- [Learn more about the Clever Cloud Kubernetes Operator](/doc/kubernetes/operator/) diff --git a/data/kubernetes_versions.yml b/data/kubernetes_versions.yml new file mode 100644 index 000000000..b2e2d7cd6 --- /dev/null +++ b/data/kubernetes_versions.yml @@ -0,0 +1,12 @@ +# Clever Kubernetes Engine — supported Kubernetes versions. +# Support policy mirrors upstream Kubernetes (n-2 from the current release). +# Update this file when a new Kubernetes version becomes available or when the +# platform default rolls forward. +current: "1.36" +default: "1.35" +supported: + - "1.36" + - "1.35" + - "1.34" +unsupported: + - "1.33" diff --git a/layouts/shortcodes/kubernetes_version.html b/layouts/shortcodes/kubernetes_version.html new file mode 100644 index 000000000..0da98df40 --- /dev/null +++ b/layouts/shortcodes/kubernetes_version.html @@ -0,0 +1,3 @@ +{{- $key := .Get 0 -}} +{{- $value := index .Site.Data.kubernetes_versions $key -}} +{{- if reflect.IsSlice $value -}}{{ delimit $value ", " }}{{- else -}}{{ $value }}{{- end -}} From dee28062caff7217ac2b4a65d29c883cb65768a5 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 27 Apr 2026 17:09:25 +0200 Subject: [PATCH 006/214] kubernetes: Developers > Essential --- content/doc/kubernetes/_index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index 4563066e8..b9f090056 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -91,7 +91,7 @@ You choose how the control plane is distributed at creation time. The topology d |Topology|Customers|VMs you pay for|Flavors|Default node group included?|Typical use case| |---|---|---|---|---|---| -|`ALL_IN_ONE`|Developers|`replicationFactor` × bundle VM (control plane + integrated worker node). Additional node groups billed separately|`S`, `M`, `L`, `XL`|No node group, but each bundle VM is also a worker node — additional node groups optional|Development, testing, small single-team clusters| +|`ALL_IN_ONE`|Essential|`replicationFactor` × bundle VM (control plane + integrated worker node). Additional node groups billed separately|`S`, `M`, `L`, `XL`|No node group, but each bundle VM is also a worker node — additional node groups optional|Development, testing, small single-team clusters| |`DEDICATED_COMPUTE`|Business|`replicationFactor` × control plane VM **plus** every worker VM in your node groups|`XS`, `S`, `M`, `L`, `XL`|No — provision a node group at creation with `--nodegroup` or later|Production clusters where control plane and workloads stay isolated| |`DISTRIBUTED`|Enterprise|For each of 5 components: `replicationFactor` × component VM **plus** every worker VM in your node groups|`2XS`, `XS`, `S`, `M`, `L`, `XL`|No — provision a node group at creation or later|Demanding production clusters needing fine-grained HA per control plane component| @@ -101,7 +101,7 @@ In `ALL_IN_ONE`, every VM in the bundle is also a worker node — the bundle cov Whenever a cluster spans multiple VMs — additional node groups on `ALL_IN_ONE`, the control plane and workers in `DEDICATED_COMPUTE`, the five components and workers in `DISTRIBUTED` — the VMs talk to each other over a [Network Group](/doc/develop/network-groups/), a private WireGuard-based mesh provisioned automatically with the cluster. Inter-component traffic stays on the private network. -**Developers (`ALL_IN_ONE`)** — one VM hosts the five control plane components plus an integrated worker node, replicated `replicationFactor` times. Additional node groups (optional) can be attached to scale compute beyond the bundle and are billed at the [node group rate](#node-groups-workers). +**Essential (`ALL_IN_ONE`)** — one VM hosts the five control plane components plus an integrated worker node, replicated `replicationFactor` times. Additional node groups (optional) can be attached to scale compute beyond the bundle and are billed at the [node group rate](#node-groups-workers). ```mermaid flowchart LR @@ -460,7 +460,7 @@ In practice: - **`DEDICATED_COMPUTE`** — `replicationFactor` × the control plane VM price, **plus** the [node group rate](#node-groups-workers) for every worker VM in your node groups. - **`DISTRIBUTED`** — for each of the five components, `replicationFactor` × the component VM price (each component picks its own flavor and replication factor), **plus** the [node group rate](#node-groups-workers) for every worker VM in your node groups. -### Developers (`ALL_IN_ONE`) bundle +### Essential (`ALL_IN_ONE`) bundle The bundle hosts the five control plane components and an integrated worker node on the same VMs. The price below covers everything; additional node groups attached to an `ALL_IN_ONE` cluster are billed separately at the [node group rate](#node-groups-workers). From dd4a106e26dc084488a998f5d25ef889067e2090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Collignon-Ducret=20R=C3=A9mi?= Date: Mon, 27 Apr 2026 18:47:41 +0200 Subject: [PATCH 007/214] changelog: announce logs API v2 decommission on May 23rd 2026 (#923) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 What does this PR do? _Briefly describe your changes and why they are needed_ ## 🔗 Related Issue (if applicable) - Closes # - Related to # --- ## 🧪 Type of Change - [ ] ⚠️ Bug fix - [ ] 📅 Changelog update - [ ] 📚 Documentation update - [ ] ✨ New content/feature - [ ] 🔧 Technical/maintenance --- ## ✅ Quick Checklist - [ ] I have read the [contributing guidelines](https://github.com/CleverCloud/documentation/blob/main/CONTRIBUTING.md) - [ ] The content is accurate and links work - [ ] The site builds without errors --- ## 👥 Reviewers @CleverCloud/reviewers ---
📋 For major changes (click to expand) ### Additional testing performed _Describe any specific testing done for complex changes_ ### Screenshots _Add screenshots for visual/layout changes_ ### Breaking changes _List any breaking changes or migration notes_
--------- Co-authored-by: Steven Le Roux Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../2026/04-23-logs-v2-deprecation.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 content/changelog/2026/04-23-logs-v2-deprecation.md diff --git a/content/changelog/2026/04-23-logs-v2-deprecation.md b/content/changelog/2026/04-23-logs-v2-deprecation.md new file mode 100644 index 000000000..58969803e --- /dev/null +++ b/content/changelog/2026/04-23-logs-v2-deprecation.md @@ -0,0 +1,26 @@ +--- +title: Decommissioning Logs API v2 endpoints +date: 2026-04-23 +description: Decommission of the legacy Logs API v2 endpoints on May 23rd, 2026. Migrate to the v4 API to ensure uninterrupted access to your application logs. +tags: + - api +authors: + - name: miton18 + link: https://github.com/miton18 + image: https://github.com/miton18.png?size=40 +excludeSearch: true +--- + +Clever Cloud is decommissioning the legacy Logs API v2 endpoints on **May 23rd, 2026**. The v4 API has been available for some time and provides a more reliable and consistent interface to access your application logs. If you still rely on v2 endpoints to fetch logs, migrate to the v4 API as soon as possible. + +- **May 23rd, 2026**: v2 logs endpoints will be permanently disabled. Any integration still using v2 will stop receiving logs. + +## How to migrate + +The v4 Logs API exposes the same capabilities with an improved interface. Update your HTTP calls to target the v4 endpoints documented in the [Logs section of the v4 API reference](/api/v4#logs). Authentication and query parameters follow the same conventions as the rest of the v4 API. + +After migrating, your integration can continue fetching logs via v4. + +If you have any questions or need help with the migration, contact [our support team](https://console.clever-cloud.com/ticket-center-choice). + +- [Logs API v4 reference](/api/v4#logs) From 6155ca74820268eb4c19417053bc7a1ac1560ac6 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 27 Apr 2026 19:31:21 +0200 Subject: [PATCH 008/214] api(v4): update routes, payloads --- content/api/v4.md | 164 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 127 insertions(+), 37 deletions(-) diff --git a/content/api/v4.md b/content/api/v4.md index 70859e23f..93f659c60 100644 --- a/content/api/v4.md +++ b/content/api/v4.md @@ -216,85 +216,163 @@ Answers with logs in a SSE (Server-Sent Events) stream. You can use optional par ### Create a logs drain -- `/v4/drains/organisations/{ownerId}/applications/{applicationId}/drains` +- `/v4/drains/organisations/{ownerId}/resources/{resourceId}/drains` - Type: `POST` ```json { "kind": "ACCESSLOG|LOG", "recipient": { - "url": "", - "type":"" - // Optional parameters, depending on recipient_type + "type": "", + "url": "" + // Additional fields, depending on recipient_type } } ``` -| Parameter | Type | Comment | -| :--- | :--- | :--- | -| `type` | `string` | `DatadogRecipient`, `ElasticsearchRecipient`, `NewRelicRecipient`, `OVHTCPRecipient`, `RawRecipient` | -| `username` | `string` | Basic Auth username | -| `password` | `string` | Basic Auth password | -| `index` | `string` | ElasticSearch index | -| `apiKey` | `string` | NewRelic API key | -| `token` | `string` | OVHcloud token | -| `rfc5424StructuredDataParameters` | `string` | OVHcloud RFC 5424 structured data parameters | +The `recipient.type` discriminates the payload shape. Allowed values and their fields: + +| `type` | Required (besides `type` and `url`) | Optional | +| :--- | :--- | :--- | +| `DATADOG` | — | — | +| `ELASTICSEARCH` | `index` | `username`, `password`, `tlsVerification` (`DEFAULT` or `TRUSTFUL`) | +| `NEWRELIC` | `apiKey` | — | +| `OVH_TCP` | — | `token`, `rfc5424StructuredDataParameters` | +| `RAW_HTTP` | — | `username`, `password` | +| `SYSLOG_TCP` | — | `rfc5424StructuredDataParameters` | +| `SYSLOG_UDP` | — | `rfc5424StructuredDataParameters` | + +Field reference: + +| Field | Type | Comment | +| :--- | :--- | :--- | +| `username` | `string` | Basic Auth username (`ELASTICSEARCH`, `RAW_HTTP`) | +| `password` | `string` | Basic Auth password (`ELASTICSEARCH`, `RAW_HTTP`) | +| `index` | `string` | Elasticsearch index name | +| `tlsVerification` | `string` | Elasticsearch TLS mode, `DEFAULT` (verify) or `TRUSTFUL` (skip verification) | +| `apiKey` | `string` | New Relic API key | +| `token` | `string` | OVHcloud Logs Data Platform token | +| `rfc5424StructuredDataParameters` | `string` | RFC 5424 structured data, e.g. `X-OVH-TOKEN="…"` | ### List logs drains -- `/v4/drains/organisations/{ownerId}/applications/{applicationId}/drains` +- `/v4/drains/organisations/{ownerId}/resources/{resourceId}/drains` - Type: `GET` +Optional query parameters: `status`, `executionStatus`, `executionStatusNotIn`. + ### Manage a logs drain -- `/v4/drains/organisations/{ownerId}/applications/{applicationId}/drains/{drainId}` +- `/v4/drains/organisations/{ownerId}/resources/{resourceId}/drains/{drainId}` - Type: `GET`/`DELETE` +### Enable/disable a logs drain + +- `/v4/drains/organisations/{ownerId}/resources/{resourceId}/drains/{drainId}/enable` +- `/v4/drains/organisations/{ownerId}/resources/{resourceId}/drains/{drainId}/disable` +- Type: `PUT` + +### Test or reset a logs drain + +- `/v4/drains/organisations/{ownerId}/resources/{resourceId}/drains/{drainId}/test-command` +- Type: `GET` — returns a `curl` command to test connectivity to the drain endpoint. +- `/v4/drains/organisations/{ownerId}/resources/{resourceId}/drains/{drainId}/reset-cursor` +- Type: `PATCH` — restarts log forwarding from the latest position. + ## Operators A Clever Cloud Operator deploys and manage resources for a given service. Available operators are: `keycloak`, `matomo`, `metabase`, `otoroshi`. - `/v4/addon-providers/addon-{operator_name}/addons/{operator_id}` - Type: `GET` -Example response: -```bash +Example response (Otoroshi — the richest payload): +```json { - "resourceId": "real_id", - "addonId": "addon_id", + "resourceId": "otoroshi_", + "addonId": "addon_", "name": "resource_name", "ownerId": "user_or_org_id", "plan": "PLAN_NAME", "version": "x.y.z", - "javaVersion": "X", # Optional - "phpVersion": "X", # Optional + "javaVersion": "X", "accessUrl": "https://service-web-ui-id.services.clever-cloud.com", - "initialCredentials": { # Optional + "availableVersions": ["x.y.z", "…"], + "initialCredentials": { "user": "user_name", "password": "user_password" }, - "api": { # Optional + "api": { "url": "https://service-api-id.services.clever-cloud.com", - … + "user": "api_user", + "secret": "api_secret", + "openapi": "https://service-api-id.services.clever-cloud.com/api/openapi.json", + "swaggerUrl": "https://service-api-id.services.clever-cloud.com/api/openapi/ui" }, - "availableVersions": [ - "x.y.z", - … - ], "resources": { - "entrypoint": "app_id", - … + "entrypoint": "app_", + "redisId": "redis_", + "pulsarId": "pulsar_", + "elasticId": "elasticsearch_", + "cellarId": "cellar_" }, "features": { - … + "networkGroup": { "id": "ng_" } } } ``` +Field availability per operator: + +All operators always return: `resourceId`, `addonId`, `name`, `ownerId`, `plan`, `version`, `accessUrl`, `availableVersions`, `resources`. + +Other fields depend on the operator (`✓` = always returned, `—` = not returned, `?` = returned when set): + +| Field | `otoroshi` | `keycloak` | `matomo` | `metabase` | +| :--- | :---: | :---: | :---: | :---: | +| `javaVersion` | ✓ | ✓ | — | ✓ | +| `phpVersion` | — | — | ✓ | — | +| `initialCredentials` | ✓ | ✓ | — | — | +| `api` | ✓ | — | — | — | +| `features.networkGroup` | ? | ? | — | — | + +For Otoroshi, `resources.entrypoint` and `resources.redisId` are always returned; `pulsarId`, `elasticId` and `cellarId` only appear when the corresponding resource exists. When the Network Group feature is enabled, `features.networkGroup` returns `{ "id": "ng_" }`; otherwise it is `null`. + - `/v4/addon-providers/addon-{operator_name}/addons/{operator_id}/reboot` - `/v4/addon-providers/addon-{operator_name}/addons/{operator_id}/rebuild` - Type: `POST` - Response code: `204` +### Version management + +Available for `keycloak`, `metabase` and `otoroshi`. + +- `/v4/addon-providers/addon-{operator_name}/addons/{operator_id}/version/check` +- Type: `GET` + +```json +{ + "installed": "x.y.z", + "latest": "x.y.z", + "available": ["x.y.z", "…"], + "needUpdate": boolean +} +``` + +- `/v4/addon-providers/addon-{operator_name}/addons/{operator_id}/version/update` +- Type: `POST` + +```json +{ + "targetVersion": "x.y.z" +} +``` + +### Otoroshi configuration export + +- `/v4/addon-providers/addon-otoroshi/addons/{operator_id}/config.yaml` +- Type: `GET` +- Response: `application/yaml` — the full `otoroshictl`-compatible configuration of the instance. + ### Network Groups You can add the deployed service main application in a [Network Group](/doc/develop/network-groups/) to activate some enhanced features: - `keycloak`: [Secured Multi Instances feature](/doc/addons/keycloak/#secured-multi-instances) @@ -329,6 +407,13 @@ If no Network Group is found, the `networkGroup` feature value is `null`: - `/v4/products/zones/{name}` - Type: `GET` +You can use optional query parameters to filter the results: + +| Parameter | Type | Comment | +| :--- | :--- | :--- | +| `tag` | `string` | Filter zones on tags (e.g. `infra:clever-cloud`) | +| `ownerId` | `string` | Restrict to zones the given owner has access to | + Example response, for each zone: ```bash [ @@ -355,17 +440,22 @@ Example response, for each zone: Define or get offload/retention policies of a [Pulsar add-on](/doc/addons/pulsar). Retention is how long messages are kept in the topic. Offload is how long messages are kept in the hot storage (NVMe SSD) before being moved to Cellar object storage (S3 compatible, HDD). Example query/response: -```bash +```json { "retentionPolicies": { - "sizeInMB": number, - "durationInDays": number + "sizeInBytes": number, + "durationInMinutes": number }, "offloadPolicies": { - "durationInDays": number, - "sizeInMB": number + "sizeInBytes": number, + "durationInMinutes": number } } ``` -A `null` value means an infinite retention or no offload. +| Field | Type | Comment | +| :--- | :--- | :--- | +| `sizeInBytes` | `int64` | Size threshold in bytes. Optional. A negative value means no limit. | +| `durationInMinutes` | `int64` | Duration threshold in minutes. Optional. A negative value means no limit. | + +When both fields are omitted (or `null`), retention is infinite and offload is disabled. From 046b97e7796000e1ca2de91354b82a197fae579c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 28 Apr 2026 11:33:56 +0200 Subject: [PATCH 009/214] changelog: Clever Tools 4.9 --- .../changelog/2026/04-28-clever-tools-4.9.md | 113 +++++++ content/doc/cli/kubernetes.md | 202 +++++++++--- content/doc/kubernetes/_index.md | 7 +- content/doc/reference/cli.md | 306 +++++++++++++++++- 4 files changed, 579 insertions(+), 49 deletions(-) create mode 100644 content/changelog/2026/04-28-clever-tools-4.9.md diff --git a/content/changelog/2026/04-28-clever-tools-4.9.md b/content/changelog/2026/04-28-clever-tools-4.9.md new file mode 100644 index 000000000..9f7eb294d --- /dev/null +++ b/content/changelog/2026/04-28-clever-tools-4.9.md @@ -0,0 +1,113 @@ +--- +title: "Clever Tools 4.9: full Kubernetes lifecycle from the CLI" +date: 2026-04-28 +description: Clever Tools 4.9 expands the Kubernetes command set with node groups, version updates, quotas and activity, and adds Swagger UI access for Otoroshi +tags: + - clever-tools + - cli + - kubernetes + - otoroshi +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 + - name: Hubert Sablonnière + link: https://github.com/hsablonniere + image: https://github.com/hsablonniere.png?size=40 +excludeSearch: true +--- + +[Clever Tools 4.9.0](https://github.com/CleverCloud/clever-tools/releases/tag/4.9.0) is available. This release significantly extends the `clever k8s` command set to cover the full lifecycle of [Clever Kubernetes Engine](/doc/kubernetes/), as it's now in public Beta. It also adds Swagger UI access to Otoroshi services and ships smaller quality-of-life improvements. + +## Kubernetes lifecycle management + +The `clever k8s` command set now covers cluster creation with detailed topology, ongoing operations, node group management, version upgrades and quota visibility. The `create` command accepts `--topology`, `--flavor`, `--cluster-version`, `--replication-factor`, `--autoscaling`, `--persistent-storage` and `--nodegroup` to provision a cluster matching your needs in a single command. The `get` output now reports topology, features, node groups and load balancers. + +```bash +# Enable access to the k8s command set +clever features enable k8s + +# Create a cluster with a control plane topology, version and initial node group +clever k8s create my-cluster \ + --topology dedicated_compute --flavor S --replication-factor 3 \ + --cluster-version 1.36 --autoscaling \ + --nodegroup XS:3 --persistent-storage + +# Inspect the cluster (topology, features, node groups, load balancers) +clever k8s get my-cluster + +# Update metadata or features +clever k8s update my-cluster --description "Production cluster" --tag env:prod,team:platform +``` + +Node groups have their own subcommands to list, create, get, update and delete, with autoscaling bounds and arbitrary tags: + +```bash +# Create an autoscaling node group +clever k8s nodegroups create my-cluster workers XS:3 --autoscaling --min 3 --max 10 + +# List node groups attached to a cluster +clever k8s nodegroups list my-cluster + +# Update bounds or target count +clever k8s nodegroups update my-cluster workers --count 5 + +# Delete a node group +clever k8s nodegroups delete my-cluster workers --yes +``` + +Version management is now first-class. The `version` subcommand reports the installed version and offers to upgrade to the latest available release when the cluster is outdated. The `version update` subcommand upgrades a cluster to an explicit target version when you prefer to drive the upgrade yourself: + +```bash +# Check the current version and prompt for an upgrade if available +clever k8s version my-cluster + +# Upgrade the cluster to a target version +clever k8s version update my-cluster --target 1.36 +``` + +Two more commands round out the set: `clever k8s activity` shows recent deployment events of a cluster, and `clever k8s quota` reports the Kubernetes quota, current usage and remaining capacity for an organisation during public Beta. + +```bash +clever k8s activity my-cluster --limit 100 +clever k8s quota +``` + +Once your cluster reaches the `ACTIVE` state, retrieve its `kubeconfig` and drive it with `kubectl` like any other Kubernetes cluster. With `--persistent-storage` enabled at creation time, a default Ceph RBD `StorageClass` is provisioned on the cluster, so any `PersistentVolumeClaim` you create is bound automatically: + +```bash +# Generate the kubeconfig and set it as your current context +clever k8s get-kubeconfig my-cluster > ~/.kube/config + +# Inspect cluster nodes and the persistent storage driver +kubectl get nodes +kubectl get csidrivers +kubectl get storageclasses + +# Deploy a workload and let it consume the default StorageClass +kubectl create deployment nginx --image=nginx +kubectl expose deployment nginx --port=80 --type=LoadBalancer +``` + +- [Learn more about Clever Kubernetes Engine](/doc/kubernetes/) + +## Otoroshi Swagger UI + +You can now open the Otoroshi Swagger UI directly from the CLI with a new `clever otoroshi open swaggerui` subcommand. The `clever otoroshi get` command also exposes the Swagger URL alongside the other endpoints, so you can hand it over to teammates or automation without opening the Console first. + +```bash +# Open the Swagger UI of an Otoroshi service in your browser +clever otoroshi open swaggerui my-otoroshi + +# Get service details, including the Swagger URL +clever otoroshi get my-otoroshi +``` + +## How to upgrade + +To upgrade Clever Tools, [use your favourite package manager](/doc/cli/install/). For example with `npm`: + +```bash +npm update -g clever-tools +clever version +``` diff --git a/content/doc/cli/kubernetes.md b/content/doc/cli/kubernetes.md index 3d5cb6175..ca3507c37 100644 --- a/content/doc/cli/kubernetes.md +++ b/content/doc/cli/kubernetes.md @@ -2,7 +2,7 @@ type: docs linkTitle: Kubernetes title: Clever Kubernetes Engine (CKE) -description: Create and manage Kubernetes clusters using Clever Cloud Kubernetes Engine from Clever Tools +description: Create and manage Kubernetes clusters using Clever Kubernetes Engine from Clever Tools keywords: - kubernetes - cke @@ -10,92 +10,216 @@ keywords: - etcd - k8s - cluster +- topology +- node group +- autoscaling - csi - persistent storage - kubeconfig - load balancers +- quota +- version --- -Clever Cloud allows you to create and manage Kubernetes clusters directly from Clever Tools. Once created and configured, you can use them with `kubectl` or any Kubernetes-compatible tool. +Clever Tools 4.9+ exposes the full lifecycle of [Clever Kubernetes Engine](/doc/kubernetes/): cluster creation with detailed topology, ongoing operations, node group management, version upgrades and quota visibility. Once a cluster is `ACTIVE`, you drive it with `kubectl` like any other Kubernetes cluster. - [Learn more about Kubernetes on Clever Cloud](/doc/kubernetes/) ## Prerequisites -Activate `k8s` feature flag to manage Kubernetes clusters: +Activate the `k8s` feature flag once per user account: -``` +```bash clever features enable k8s ``` -Then, check it works with the following command: +Check the command set is available: -``` +```bash clever k8s ``` -In all the following examples, you can target a specific organisation with the `--org` or `-o` option. +In all examples below, target a specific organisation with the `--org` (or `-o`) option. Output format defaults to a human-readable table; pass `--format json` (or `-F json`) on read commands when you need structured output for scripts or pipelines. -## Create/Delete a Cluster +## Create a cluster -To create a Kubernetes cluster, you just need a name and you can wait for it to be in `ACTIVE` state: -``` -clever k8s create myKubeCluster -clever k8s create myKubeCluster --watch +The fastest way to create a cluster is to provide only a name. The platform picks `ALL_IN_ONE` as the default topology, the smallest available flavor (`S`) and a replication factor of `1`: + +```bash +clever k8s create myCluster --org ``` -To delete a cluster, use: +Add `--watch` to follow the deployment until the cluster reaches `ACTIVE`: + +```bash +clever k8s create myCluster --watch --org ``` -clever k8s delete myKubeCluster -clever k8s delete myKubeCluster --yes + +When you need a specific shape, combine topology, flavor, replication factor, version and an initial node group in a single command. Topology values (`all_in_one`, `dedicated_compute`, `distributed`) are accepted in lowercase or uppercase. The `--nodegroup :` option provisions an initial node group named `default`, ready to schedule workloads as soon as the cluster reaches `ACTIVE`. Use it on `dedicated_compute` and `distributed` clusters, which otherwise come up with no worker. `all_in_one` bundles already include an integrated worker on each bundle VM, so passing `--nodegroup` adds an *extra* pool — Clever Tools warns you and asks for confirmation in that case: + +```bash +clever k8s create myCluster --org \ + --topology dedicated_compute --flavor S --replication-factor 3 \ + --cluster-version 1.36 \ + --description "Production cluster" \ + --tag env:prod,team:platform \ + --autoscaling \ + --persistent-storage \ + --nodegroup M:3 ``` -## List Clusters +The `--cluster-version` value is validated against the platform-supported versions before the API call; an unsupported value (e.g. `0.99`) is rejected upfront with the list of available versions. -If you have cluster, you can list them and know their name, ID and status with: +## List, get and inspect -``` +List the Kubernetes clusters of the active organisation: + +```bash clever k8s list clever k8s list --format json ``` -## Get Cluster information +Get full details for one cluster (topology, features, node groups, load balancers, storage usage). The first form resolves a cluster by name when unambiguous; the second targets a specific cluster by ID: + +```bash +clever k8s get myCluster +clever k8s get kubernetes_id -F json +``` -To get information about a specific cluster, use: +The human format shows topology and feature state in a single table, with extra tables for control plane components (in `DISTRIBUTED`), node groups and load balancers when present: +```text +┌────────────────────┬─────────────────────────────────────────┐ +│ (index) │ Values │ +├────────────────────┼─────────────────────────────────────────┤ +│ Name │ 'myCluster' │ +│ ID │ 'kubernetes_id' │ +│ Status │ 'ACTIVE' │ +│ Version │ '1.36' │ +│ Topology │ 'DEDICATED_COMPUTE (S, rf=3)' │ +│ Autoscaling │ 'enabled' │ +│ Persistent storage │ 'enabled' │ +│ Tags │ 'env:prod, team:platform' │ +│ Description │ 'Production cluster' │ +└────────────────────┴─────────────────────────────────────────┘ ``` -clever k8s get myKubeCluster -clever k8s get kubernetes_id -F json + +## Update cluster metadata + +Rename the cluster, change its description or tags, and toggle autoscaling without redeploying. Pass at least one of `--name`, `--description`, `--tag`, `--autoscaling` or `--disable-autoscaling`. The `--autoscaling` and `--disable-autoscaling` flags are mutually exclusive: + +```bash +clever k8s update myCluster --description "Production cluster, EU" --tag env:prod,team:platform +clever k8s update myCluster --name myCluster-eu +clever k8s update myCluster --autoscaling +clever k8s update myCluster --disable-autoscaling +``` + +Updating `--tag` replaces the full list of tags. Pass an empty value to clear them. + +## Add persistent storage + +Enable persistent storage on an existing cluster with a Ceph RBD CSI driver. The cluster gains a default `StorageClass` named `csi-rbd-sc`, ready to back any `PersistentVolumeClaim`: + +```bash +clever k8s add-persistent-storage myCluster +``` + +Persistent storage is a one-way toggle; once enabled, it cannot be removed from a running cluster. Create a new cluster without `--persistent-storage` if you no longer need it. + +## Get the kubeconfig file + +Retrieve the kubeconfig of an `ACTIVE` cluster. Wait for the cluster to reach `ACTIVE` before redirecting the output to a file — the command is a no-op on non-ready clusters: + +```bash +clever k8s get-kubeconfig myCluster +clever k8s get-kubeconfig myCluster > ~/.kube/config +``` + +Once the kubeconfig is in place, drive the cluster with `kubectl` as usual. With `--persistent-storage` enabled, the default `StorageClass` is provisioned automatically. `kubectl get nodes` lists the integrated workers immediately on `all_in_one` clusters; on `dedicated_compute` and `distributed` clusters, the list stays empty until you add a node group: + +```bash +kubectl get nodes +kubectl get csidrivers +kubectl get storageclasses + +kubectl create deployment nginx --image=nginx +kubectl expose deployment nginx --port=80 --type=LoadBalancer ``` -Classic response is a table: +## Activity and quota + +`activity` lists recent deployment events of a cluster (creation steps, control plane rollout, node group resizes). The default limit is `50`; pass `--limit N` (between `1` and `1000`) to widen the window: +```bash +clever k8s activity myCluster +clever k8s activity myCluster --limit 100 +clever k8s activity myCluster -F json ``` -┌─────────┬─────────────────────────────────────────┐ -│ (index) │ Values │ -├─────────┼─────────────────────────────────────────┤ -│ Name │ 'myKubeCluster' │ -│ ID │ 'kubernetes_id' │ -│ Version │ 1.34.1 │ -│ Status │ 'ACTIVE' │ -└─────────┴─────────────────────────────────────────┘ + +`quota` reports the Kubernetes quota, current usage and remaining capacity for the active organisation. Each organisation starts with **40 vCPU and 40 GB of RAM** during the public Beta: + +```bash +clever k8s quota +clever k8s quota -F json ``` -## Add persistent storage to a Cluster +## Cluster version -You can add persistent storage to an `ACTIVE` cluster with: +Report the installed Kubernetes version of a cluster, and offer an interactive upgrade prompt when an upgrade is available: +```bash +clever k8s version myCluster +clever k8s version check myCluster ``` -clever k8s add-persistent-storage myKubeCluster + +Drive the upgrade explicitly to a target version. The target is validated against the supported versions before the API call: + +```bash +clever k8s version update myCluster --target 1.36 ``` -Once added you can't remove it, but you can start a fresh cluster without persistent storage. +## Node groups -## Get kubeconfig file of a Cluster +A node group is a pool of worker VMs of the same flavor. `dedicated_compute` and `distributed` clusters need at least one node group to schedule workloads; `all_in_one` clusters already include an integrated worker on each bundle VM and only need a node group when you want extra capacity beyond the bundle. Create one alongside a cluster (with `--nodegroup` on `clever k8s create`) or independently with `nodegroups create`. The third positional argument follows the `:` format and accepts lowercase flavors: -To get the `kubeconfig` file of an `ACTIVE` cluster, and set it as the current context, use: +```bash +clever k8s nodegroups create myCluster workers XS:3 +clever k8s nodegroups create myCluster workers XS:3 --autoscaling --min 3 --max 10 +clever k8s nodegroups create myCluster workers XS:3 --description "GPU-intensive workers" --tag env:prod +``` + +Inspect node groups attached to a cluster: + +```bash +clever k8s nodegroups list myCluster +clever k8s nodegroups list myCluster -F json +clever k8s nodegroups get myCluster workers +clever k8s nodegroups get myCluster node_group_id +``` + +Update bounds, target count, autoscaling state or metadata. Pass at least one of `--count`, `--min`, `--max`, `--autoscaling`, `--disable-autoscaling`, `--description` or `--tag`. Resizes are queued: the API rejects a second update while a previous resize is still running: +```bash +clever k8s nodegroups update myCluster workers --count 5 +clever k8s nodegroups update myCluster workers --autoscaling --min 2 --max 10 +clever k8s nodegroups update myCluster workers --disable-autoscaling +clever k8s nodegroups update myCluster workers --description "Updated description" ``` -clever k8s get-kubeconfig myKubeCluster -clever k8s get-kubeconfig myKubeCluster > ~/.kube/config + +Delete a node group; nodes are drained and the underlying VMs are removed. Skip the confirmation prompt with `--yes`: + +```bash +clever k8s nodegroups delete myCluster workers +clever k8s nodegroups delete myCluster workers --yes +``` + +## Delete a cluster + +Delete a cluster by name (when unambiguous) or by ID. Skip the confirmation prompt with `--yes`: + +```bash +clever k8s delete myCluster +clever k8s delete myCluster --yes +clever k8s delete kubernetes_id --yes ``` diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index b9f090056..6a239be84 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -197,7 +197,7 @@ When you need a specific shape, combine topology, flavor and replication factor: ```bash clever k8s create myCluster \ - --topology DEDICATED_COMPUTE --flavor S --replication-factor 3 \ + --topology dedicated_compute --flavor S --replication-factor 3 \ --cluster-version 1.36 \ --description "Production cluster" \ --tag env:prod,team:platform \ @@ -312,11 +312,10 @@ Once enabled, the autoscaler reacts to node-level resource pressure (CPU and mem You can use Clever Cloud block storage to attach a persistent volume to a cluster through a CSI (Container Storage Interface). Enable persistent storage at creation with `--persistent-storage`, or on an existing cluster: ```bash -clever k8s update clusterNameOrId --persistent-storage --org clever k8s add-persistent-storage clusterNameOrId --org ``` -Both commands reach the same result. Persistent storage is currently a one-way toggle: once enabled, it cannot be removed from a running cluster. Create a new cluster without `--persistent-storage` if you no longer need it. +Persistent storage is currently a one-way toggle: once enabled, it cannot be removed from a running cluster. Create a new cluster without `--persistent-storage` if you no longer need it. ## Get the kubeconfig file @@ -332,7 +331,7 @@ You can directly save it as your local kubeconfig file: clever k8s get-kubeconfig clusterNameOrId --org > ~/.kube/config ``` -Check everything is working by listing the nodes of your cluster (it should be empty at this point): +Check everything is working by listing the nodes of your cluster. With `ALL_IN_ONE`, the integrated worker on each bundle VM appears immediately. With `DEDICATED_COMPUTE` or `DISTRIBUTED`, the list stays empty until you provision a node group: ```bash # With the default kubeconfig file: diff --git a/content/doc/reference/cli.md b/content/doc/reference/cli.md index c248444df..7122e2a47 100644 --- a/content/doc/reference/cli.md +++ b/content/doc/reference/cli.md @@ -367,7 +367,7 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Options** ``` -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) --y, --yes Skip confirmation and proceed with deletion directly +-y, --yes Skip confirmation prompts ``` ### addon env @@ -1493,6 +1493,29 @@ clever help clever k8s ``` +### k8s activity + +**Description:** Show recent deployment events of a Kubernetes cluster + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s activity [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +``` + +**Options** +``` +-F, --format Output format (human, json) (default: human) + --limit Number of events to fetch (1 to 1000) (default: 50) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) +``` + ### k8s add-persistent-storage **Description:** Activate persistent storage to a deployed Kubernetes cluster @@ -1527,13 +1550,23 @@ clever k8s create [options] **Arguments** ``` -cluster-name Kubernetes cluster name +cluster-name Kubernetes cluster name ``` **Options** ``` --o, --org, --owner Organisation to target by its ID (or name, if unambiguous) --w, --watch Watch the deployment until the cluster is deployed + --autoscaling Enable the cluster autoscaler + --cluster-version Kubernetes version to deploy (e.g.: 1.36) + --description Free-form cluster description + --flavor Control plane flavor + --nodegroup Initial node group (format: :, e.g.: XS:3) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) + --persistent-storage Enable persistent storage (Ceph CSI) + --replication-factor Control plane replication factor + --tag Semantic tags (comma-separated, e.g.: env:prod,team:platform) + --topology Cluster topology (must be set with --flavor and --replication-factor) +-w, --watch Watch the deployment until the cluster is deployed +-y, --yes Skip confirmation prompts ``` ### k8s delete @@ -1555,7 +1588,7 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Options** ``` -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) --y, --yes Skip confirmation and proceed with deletion directly +-y, --yes Skip confirmation prompts ``` ### k8s get @@ -1618,6 +1651,251 @@ clever k8s list [options] -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` +### k8s nodegroups + +**Description:** Manage Kubernetes node groups + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s nodegroups +``` + +#### k8s nodegroups create + +**Description:** Create a node group on a Kubernetes cluster + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s nodegroups create [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +nodegroup-name Node group name (lowercase RFC 1123, max 63 chars) +flavor:count Node group flavor and target node count (format: :, e.g.: XS:3) +``` + +**Options** +``` + --autoscaling Enable cluster autoscaler for this node group (requires --min and --max) + --description Free-form node group description + --max Maximum node count when autoscaling is enabled + --min Minimum node count when autoscaling is enabled +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) + --tag Arbitrary tag attached to the node group +``` + +#### k8s nodegroups delete + +**Description:** Delete a node group from a Kubernetes cluster + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s nodegroups delete [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +nodegroup-id|nodegroup-name Kubernetes node group ID or name +``` + +**Options** +``` +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) +-y, --yes Skip confirmation prompts +``` + +#### k8s nodegroups get + +**Description:** Get information about a Kubernetes node group + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s nodegroups get [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +nodegroup-id|nodegroup-name Kubernetes node group ID or name +``` + +**Options** +``` +-F, --format Output format (human, json) (default: human) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) +``` + +#### k8s nodegroups list + +**Description:** List the node groups of a Kubernetes cluster + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s nodegroups list [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +``` + +**Options** +``` +-F, --format Output format (human, json) (default: human) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) +``` + +#### k8s nodegroups update + +**Description:** Update a node group on a Kubernetes cluster + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s nodegroups update [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +nodegroup-id|nodegroup-name Kubernetes node group ID or name +``` + +**Options** +``` + --autoscaling Enable the cluster autoscaler + --count Target node count + --description Free-form node group description + --disable-autoscaling Disable the cluster autoscaler + --max Maximum node count (autoscaling bound) + --min Minimum node count (autoscaling bound) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) + --tag Arbitrary tag attached to the node group +``` + +### k8s quota + +**Description:** Get the Kubernetes quota, usage and remaining of an organisation + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s quota [options] +``` + +**Options** +``` +-F, --format Output format (human, json) (default: human) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) +``` + +### k8s update + +**Description:** Update a Kubernetes cluster metadata or features + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s update [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +``` + +**Options** +``` + --autoscaling Enable the cluster autoscaler + --description Free-form cluster description + --disable-autoscaling Disable the cluster autoscaler + --name Rename the cluster +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) + --tag Replace tags (comma-separated, e.g.: env:prod,team:platform) +``` + +### k8s version + +**Description:** Check a Kubernetes cluster deployed version + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s version [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +``` + +**Options** +``` +-F, --format Output format (human, json) (default: human) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) +``` + +#### k8s version check + +**Description:** Check a Kubernetes cluster deployed version + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s version check [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +``` + +**Options** +``` +-F, --format Output format (human, json) (default: human) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) +``` + +#### k8s version update + +**Description:** Update a Kubernetes cluster to a target version + +**Since:** 4.9.0 + +**Usage** +``` +clever k8s version update [options] +``` + +**Arguments** +``` +cluster-id|cluster-name Kubernetes cluster ID or name +``` + +**Options** +``` +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) + --target Target version to upgrade to (e.g.: 24, 2.4, 2.4.1) +``` + ## keycloak **Description:** Manage Clever Cloud Keycloak services @@ -2589,7 +2867,7 @@ consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) **Options** ``` --y, --yes Skip confirmation and proceed with deletion directly +-y, --yes Skip confirmation prompts ``` ### oauth-consumers get @@ -2807,6 +3085,22 @@ clever otoroshi open logs addon-id|addon-name Add-on ID (or name, if unambiguous) ``` +#### otoroshi open swaggerui + +**Description:** Open the Otoroshi Swagger UI in your browser + +**Since:** 4.9.0 + +**Usage** +``` +clever otoroshi open swaggerui +``` + +**Arguments** +``` +addon-id|addon-name Add-on ID (or name, if unambiguous) +``` + #### otoroshi open webui **Description:** Open the Otoroshi admin console in your browser From ebfdd23031555b75b2b90b3090246016aec56ceb Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 28 Apr 2026 12:07:55 +0200 Subject: [PATCH 010/214] fix: dead links --- content/changelog/2024/11-08-mysql-mysql-8.4.0.md | 2 +- content/changelog/2024/12-04-otoroshi-available.md | 6 +++--- content/changelog/2025/06-04-otoroshi-17.3.md | 2 +- content/changelog/2025/07-16-otoroshi-17.4.md | 2 +- content/changelog/2025/09-10-otoroshi-17.5.md | 2 +- content/changelog/2025/11-13-otoroshi-plugins.md | 2 +- content/changelog/2026/03-04-otoroshi-17.13.md | 2 +- content/changelog/2026/03-30-otoroshi-17.14.md | 2 +- content/doc/addons/otoroshi.md | 6 +++--- content/doc/applications/python/servers.md | 2 +- content/guides/astro.md | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/content/changelog/2024/11-08-mysql-mysql-8.4.0.md b/content/changelog/2024/11-08-mysql-mysql-8.4.0.md index b3222e81d..22003ae2c 100644 --- a/content/changelog/2024/11-08-mysql-mysql-8.4.0.md +++ b/content/changelog/2024/11-08-mysql-mysql-8.4.0.md @@ -14,7 +14,7 @@ aliases: excludeSearch: true --- -MySQL 8.4 is available on Clever Cloud. You can deploy it as a new add-on or migrate existing ones. As this is the first release (8.4.0) of this long term support (LTS) branch for the [Percona Server](https://www.percona.com/mysql/software/percona-server-for-mysql) we use, it's tagged as `early`. That means you should consider it mostly to make some tests and discover what's new. But we recommend [waiting a bit](https://www.percona.com/blog/severe-instability-of-mysql-8-0-38-8-4-1-and-9-0-resolved-in-upcoming-releases/) before using a new branch in production. +MySQL 8.4 is available on Clever Cloud. You can deploy it as a new add-on or migrate existing ones. As this is the first release (8.4.0) of this long term support (LTS) branch for the [Percona Server](https://www.percona.com/mysql/software/percona-server-for-mysql) we use, it's tagged as `early`. That means you should consider it mostly to make some tests and discover what's new. But we recommend [waiting a bit](https://www.percona.com/blog/do-not-upgrade-to-any-version-of-mysql-after-8-0-37/) before using a new branch in production. * [Learn more about MySQL 8.4](https://www.percona.com/blog/mysql-8-4-first-peek/) * [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) diff --git a/content/changelog/2024/12-04-otoroshi-available.md b/content/changelog/2024/12-04-otoroshi-available.md index 9534b12b5..ce5ea21b9 100644 --- a/content/changelog/2024/12-04-otoroshi-available.md +++ b/content/changelog/2024/12-04-otoroshi-available.md @@ -19,14 +19,14 @@ excludeSearch: true After some weeks of testing, the Clever Cloud's Otoroshi with LLM add-on, is available in public beta. Thus, you can deploy the service, from [Console](https://console.clever-cloud.com/users/me/addons/new), [API](/api) or [Clever Tools](https://github.com/CleverCloud/clever-tools), and use it in minutes. -We developed this product with its creator and core developer: [Mathieu Ancelin](https://github.com/mathieuancelin) from [Cloud APIM](https://www.cloud-apim.com/). Otoroshi is an open source reverse proxy that allows you to create your own routes, manage authentication, authorization, and rate limiting. It helps you to expose services with enterprise needs in mind, as you can manage organisations, teams, service groups, with event management, data export, multiple secret stores support, etc. And it can be managed from a web interface or [as an API](https://maif.github.io/otoroshi/manual/api.html). +We developed this product with its creator and core developer: [Mathieu Ancelin](https://github.com/mathieuancelin) from [Cloud APIM](https://www.cloud-apim.com/). Otoroshi is an open source reverse proxy that allows you to create your own routes, manage authentication, authorization, and rate limiting. It helps you to expose services with enterprise needs in mind, as you can manage organisations, teams, service groups, with event management, data export, multiple secret stores support, etc. And it can be managed from a web interface or [as an API](https://maif.github.io/otoroshi/manual/docs/api). -On Clever Cloud, it comes batteries included, pre-configured with features such as [Coraza Web Appplication Firewall](https://maif.github.io/otoroshi/manual/how-to-s/instantiate-waf-coraza.html) or [LLM extension](https://cloud-apim.github.io/otoroshi-llm-extension/docs/overview). The latter allows you to manage AI services from many providers (Anthropic, Groq, Hugging Face, Mistral, OpenAI, OVHcloud), Ollama instances or any OpenAI API compatible endpoints. +On Clever Cloud, it comes batteries included, pre-configured with features such as [Coraza Web Appplication Firewall](https://maif.github.io/otoroshi/manual/docs/tutorials/instantiate-waf-coraza) or [LLM extension](https://cloud-apim.github.io/otoroshi-llm-extension/docs/overview). The latter allows you to manage AI services from many providers (Anthropic, Groq, Hugging Face, Mistral, OpenAI, OVHcloud), Ollama instances or any OpenAI API compatible endpoints. All this with a unique interface, adding token management, rate limit, context, validation, moderation, etc. Create your own agents and flows with an only URL, distributing tokens to your team or customers, for cURL requests or integrations to many services and tools… or your own applications hosted on Clever Cloud. It's based on a Java application and a Redis® database. Once deployed you'll get a management URL with credentials. Want to learn more? Feel free to let us know what you think and ask your questions in [our GitHub Community](https://github.com/CleverCloud/Community/discussions/categories/otoroshi). -- [Learn more about Otoroshi](https://maif.github.io/otoroshi/manual/how-to-s/index.html) +- [Learn more about Otoroshi](https://maif.github.io/otoroshi/manual/docs/index.html) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) - [Otoroshi LLM extension video tutorials](https://www.youtube.com/watch?v=M8PbydxPw4A&list=PLNHaf5rXAx3FWk7dn2fKGwQXxeLCPhZCh) diff --git a/content/changelog/2025/06-04-otoroshi-17.3.md b/content/changelog/2025/06-04-otoroshi-17.3.md index aac448078..8fd608e1a 100644 --- a/content/changelog/2025/06-04-otoroshi-17.3.md +++ b/content/changelog/2025/06-04-otoroshi-17.3.md @@ -29,6 +29,6 @@ clever otoroshi version update yourOtoroshiNameOrId clever otoroshi version update yourOtoroshiNameOrId v17.3.1_1749049547 ``` -- [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/topics/workflows.html) +- [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/docs/topics/workflows) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) diff --git a/content/changelog/2025/07-16-otoroshi-17.4.md b/content/changelog/2025/07-16-otoroshi-17.4.md index 2c8ed4016..f82ce2289 100644 --- a/content/changelog/2025/07-16-otoroshi-17.4.md +++ b/content/changelog/2025/07-16-otoroshi-17.4.md @@ -27,6 +27,6 @@ clever otoroshi version update yourOtoroshiNameOrId clever otoroshi version update yourOtoroshiNameOrId v17.4.0_1752074416 ``` -- [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/topics/workflows.html) +- [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/docs/topics/workflows) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) diff --git a/content/changelog/2025/09-10-otoroshi-17.5.md b/content/changelog/2025/09-10-otoroshi-17.5.md index 0deb553e8..5b6d26c37 100644 --- a/content/changelog/2025/09-10-otoroshi-17.5.md +++ b/content/changelog/2025/09-10-otoroshi-17.5.md @@ -27,6 +27,6 @@ clever otoroshi version update yourOtoroshiNameOrId clever otoroshi version update yourOtoroshiNameOrId v17.5.1_1757489873 ``` -- [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/topics/workflows.html) +- [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/docs/topics/workflows) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) diff --git a/content/changelog/2025/11-13-otoroshi-plugins.md b/content/changelog/2025/11-13-otoroshi-plugins.md index 3bcb25514..d39ec9193 100644 --- a/content/changelog/2025/11-13-otoroshi-plugins.md +++ b/content/changelog/2025/11-13-otoroshi-plugins.md @@ -19,5 +19,5 @@ Otoroshi on Clever Cloud comes with many included plugins to manage AI based API - Add URLs pointing to the plugins JAR files, separated by a space or a new line - Rebuild the application -This works for any Otoroshi version deployed on Clever Cloud. If you want to learn more about Otoroshi plugins and how to create your own, check the [Otoroshi documentation](https://maif.github.io/otoroshi/manual/plugins/create-plugins.html). +This works for any Otoroshi version deployed on Clever Cloud. If you want to learn more about Otoroshi plugins and how to create your own, check the [Otoroshi documentation](https://maif.github.io/otoroshi/manual/docs/plugins/create-plugins). - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) diff --git a/content/changelog/2026/03-04-otoroshi-17.13.md b/content/changelog/2026/03-04-otoroshi-17.13.md index a53724a3a..f8951ad0d 100644 --- a/content/changelog/2026/03-04-otoroshi-17.13.md +++ b/content/changelog/2026/03-04-otoroshi-17.13.md @@ -15,7 +15,7 @@ authors: excludeSearch: true --- -[Otoroshi v17.13](https://github.com/MAIF/otoroshi/releases/tag/v17.13.0) is available with experimental support for the [Kubernetes Gateway API](https://maif.github.io/otoroshi/manual/topics/kubernetes-gateway-api.html), enabling standardised Kubernetes-native traffic management. This release also introduces [remote catalogs](https://maif.github.io/otoroshi/manual/topics/remote-catalogs.html), allowing to fetch and manage plugin or configuration catalogs from external sources. +[Otoroshi v17.13](https://github.com/MAIF/otoroshi/releases/tag/v17.13.0) is available with experimental support for the [Kubernetes Gateway API](https://maif.github.io/otoroshi/manual/docs/topics/kubernetes-gateway-api), enabling standardised Kubernetes-native traffic management. This release also introduces [remote catalogs](https://maif.github.io/otoroshi/manual/docs/topics/remote-catalogs), allowing to fetch and manage plugin or configuration catalogs from external sources. A webhook validator plugin is also included, providing HMAC signature verification for incoming webhook payloads. It supports multiple algorithms (SHA256, SHA512, SHA384, SHA1) and is provider-agnostic with configurable signature headers and signing templates, compatible with services such as GitHub, Stripe, Slack or YouSign. diff --git a/content/changelog/2026/03-30-otoroshi-17.14.md b/content/changelog/2026/03-30-otoroshi-17.14.md index e054921cf..460b5a83e 100644 --- a/content/changelog/2026/03-30-otoroshi-17.14.md +++ b/content/changelog/2026/03-30-otoroshi-17.14.md @@ -15,7 +15,7 @@ authors: excludeSearch: true --- -[Otoroshi v17.14](https://github.com/MAIF/otoroshi/releases/tag/v17.14.0) is available with significant enhancements to [remote catalogs](https://maif.github.io/otoroshi/manual/topics/remote-catalogs.html). They now support organisation scanning, additional GitHub-like providers, pattern-based and YAML-formatted descriptor files, as well as Kubernetes-like manifests. This release also introduces PostgreSQL as a data exporter target and adds Redis Sentinel password support with the Lettuce driver. +[Otoroshi v17.14](https://github.com/MAIF/otoroshi/releases/tag/v17.14.0) is available with significant enhancements to [remote catalogs](https://maif.github.io/otoroshi/manual/docs/topics/remote-catalogs). They now support organisation scanning, additional GitHub-like providers, pattern-based and YAML-formatted descriptor files, as well as Kubernetes-like manifests. This release also introduces PostgreSQL as a data exporter target and adds Redis Sentinel password support with the Lettuce driver. New mandatory flags are available on client certificate plugins and OIDC JWT verification for APIs, offering finer control over authentication requirements. The expression language has been improved with path-based read support for deep structures such as user profiles, with complex structure stringification. Several fixes address tunnel handler plugin visibility, Kafka data exporter host validation, and the "Override Location header" plugin behaviour. diff --git a/content/doc/addons/otoroshi.md b/content/doc/addons/otoroshi.md index 25719c2fa..72e94a4dd 100644 --- a/content/doc/addons/otoroshi.md +++ b/content/doc/addons/otoroshi.md @@ -107,7 +107,7 @@ clever otoroshi open webui myOtoroshi The first time you connect, change the initial password (Security -> Administrators -> Edit user). -* [Learn how to use Otoroshi](https://maif.github.io/otoroshi/manual/how-to-s/index.html) +* [Learn how to use Otoroshi](https://maif.github.io/otoroshi/manual/docs/index.html) ## Underlying resources @@ -155,11 +155,11 @@ The integration provides advanced security capabilities through OWASP CRS implem ### Enterprise Capabilities Designed for production environments, the Coraza WAF plugin offers flexible configuration options, supporting both detection and prevention modes. It enables customized rule sets per domain and provides detailed security event tracking through Otoroshi's event management system. The implementation is optimized for minimal performance impact while maintaining robust security controls. -- [Otoroshi Coraza WAF documentation](https://maif.github.io/otoroshi/manual/how-to-s/instantiate-waf-coraza.html) +- [Otoroshi Coraza WAF documentation](https://maif.github.io/otoroshi/manual/docs/tutorials/instantiate-waf-coraza) ## Manage Otoroshi from its API -Otoroshi exposes a comprehensive REST API that enables programmatic control over all operations available through the Otoroshi dashboard. The dashboard itself operates as a client of this API. It gives you full control over your Otoroshi instances, enabling you to build custom integrations and extensions tailored to your infrastructure needs. [A Swagger UI detailing available endpoints is available](https://maif.github.io/otoroshi/swagger-ui/index.html). +Otoroshi exposes a comprehensive REST API that enables programmatic control over all operations available through the Otoroshi dashboard. The dashboard itself operates as a client of this API. It gives you full control over your Otoroshi instances, enabling you to build custom integrations and extensions tailored to your infrastructure needs. [A Swagger UI detailing available endpoints is available](https://maif.github.io/otoroshi/manual/api-reference). An OpenAPI descriptor is available from your instance: diff --git a/content/doc/applications/python/servers.md b/content/doc/applications/python/servers.md index 947453d9f..695167877 100644 --- a/content/doc/applications/python/servers.md +++ b/content/doc/applications/python/servers.md @@ -41,7 +41,7 @@ To enable [uWSGI asynchronous](https://uwsgi-docs.readthedocs.io/en/latest/Async | Name | Description | Default | |------|-------------|---------| -| `CC_GUNICORN_WORKER_CLASS` | Type of worker to use. [Available workers](https://docs.gunicorn.org/en/stable/settings.html#worker-class) | `sync` | +| `CC_GUNICORN_WORKER_CLASS` | Type of worker to use. [Available workers](https://gunicorn.org/reference/settings/#worker_class) | `sync` | | `CC_GUNICORN_TIMEOUT` | Gunicorn timeout (in seconds) | `30` | | `CC_GUNICORN_LOGLEVEL` | Gunicorn log level | `info` | diff --git a/content/guides/astro.md b/content/guides/astro.md index f58127925..9b47cbb60 100644 --- a/content/guides/astro.md +++ b/content/guides/astro.md @@ -25,7 +25,7 @@ Clever Cloud supports deploying both [fully static and on-demand rendered](https - The `static` output mode is ideal for most content-oriented website, for which you have no need for per-visitor server-side customization. Consider using a [Static runtime](/doc/applications/static/) when using this output mode, with the automatic site generation. - The `server` or `hybrid` output modes: consider using a [Node.js runtime](/doc/applications/nodejs) with [Astro’s Node adapter](https://docs.astro.build/en/guides/integrations-guide/node/) -If you need an example source code, get [Astrowind](https://github.com/onwidget/astrowind) (you'll need [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Node.js](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs)): +If you need an example source code, get [Astrowind](https://github.com/onwidget/astrowind) (you'll need [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Node.js](https://nodejs.org/en/download)): ```bash git clone https://github.com/onwidget/astrowind myStaticApp ``` From 32c4c33217f3b22ac3a2cf4a166e0de1cc12e4b8 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 28 Apr 2026 12:17:22 +0200 Subject: [PATCH 011/214] changelog: images updates, 2026W18 --- content/changelog/2026/04-28-images-update.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 content/changelog/2026/04-28-images-update.md diff --git a/content/changelog/2026/04-28-images-update.md b/content/changelog/2026/04-28-images-update.md new file mode 100644 index 000000000..ae6183706 --- /dev/null +++ b/content/changelog/2026/04-28-images-update.md @@ -0,0 +1,26 @@ +--- +title: "Images update: Ruby 4.0.3, Git 2.54, Yarn 4.14" +description: All runtimes updated except PHP, with security and library refreshes across the board +date: 2026-04-28 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images, except PHP. Deployment is in progress for all our users. + +* **Common:** + * Linux kernel 6.19.14 + * CA certificates 20260223 + * Git 2.54.0 +* **Node.js & Bun:** + * Yarn 4.14.1 +* **Ruby:** + * Update to 3.2.11 + * Update to 3.4.9 + * Update to 4.0.3 From 5d4e12d65a7a7090d1be9d16ddbe3589fc003dcf Mon Sep 17 00:00:00 2001 From: Julien Durillon Date: Wed, 29 Apr 2026 08:27:04 +0200 Subject: [PATCH 012/214] administrate(ssl): update GPG public key for sending encrypted emails --- content/doc/administrate/ssl.md | 90 +++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/content/doc/administrate/ssl.md b/content/doc/administrate/ssl.md index fecc3c287..7e406d90a 100644 --- a/content/doc/administrate/ssl.md +++ b/content/doc/administrate/ssl.md @@ -158,12 +158,11 @@ If your are a Keybase.io user, you can find us at [keybase.io/clevercloud](https Email can be a secure way to transfer your certificates **when using a signed and encrypted email with GPG**. Our dedicated email for receiving certificates is [ssl@clever-cloud.com](mailto:ssl@clever-cloud.com). -- fingerprint: `03943517934C1FA5ED4E2F61218B86BD5278470F` -- 64-bit: `218B86BD5278470F` +- fingerprint: `0394 3517 934C 1FA5 ED4E 2F61 218B 86BD 5278 460F` +- 64-bit: `218B 86BD 5278 460F` ```bash -----BEGIN PGP PUBLIC KEY BLOCK----- -Comment: GPGTools - https://gpgtools.org mQINBFa4bJMBEAC21vsfJ1ay5iVUcKsP8X8GziZu8daV5G4Lqpns54zN/GB05f9+ 3jV1RYRMweq7RC6XU/GDZo20ksvDw0N963/WCswCt0MyzM1O105tZ/ZbYyVV/w5w @@ -177,42 +176,55 @@ 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 -=DNbu +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 +SboMCws= +=VOPS -----END PGP PUBLIC KEY BLOCK----- ``` From 5ea63a88e984c4b16fa3f71ad793909f3fb3fbe6 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 29 Apr 2026 15:27:28 +0200 Subject: [PATCH 013/214] changelog: API update, GitHub webhook secret rotation (#929) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 What does this PR do? This PR introduces a new "API update" style of changelog entry, starting with the new CC API endpoint that rotates the GitHub webhook secret of an application and its siblings. --- ## 🧪 Type of Change - [ ] ⚠️ Bug fix - [x] 📅 Changelog update - [x] 📚 Documentation update - [ ] ✨ New content/feature - [ ] 🔧 Technical/maintenance --- ## ✅ Quick Checklist - [x] I have read the [contributing guidelines](https://github.com/CleverCloud/documentation/blob/main/CONTRIBUTING.md) - [x] The content is accurate and links work - [x] The site builds without errors --- .../04-21-github-webhook-secret-rotation.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 content/changelog/2026/04-21-github-webhook-secret-rotation.md diff --git a/content/changelog/2026/04-21-github-webhook-secret-rotation.md b/content/changelog/2026/04-21-github-webhook-secret-rotation.md new file mode 100644 index 000000000..56c3448cd --- /dev/null +++ b/content/changelog/2026/04-21-github-webhook-secret-rotation.md @@ -0,0 +1,30 @@ +--- +title: "API update: rotate GitHub webhook secrets for your applications" +date: 2026-04-21 +description: A new CC API endpoint lets you rotate the GitHub webhook secret of an application and its linked siblings, without recreating the hook. +tags: + - api + - api-update + - github +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +The Clever Cloud API now exposes a dedicated endpoint to rotate the GitHub webhook secret of an application linked to a GitHub repository. + +The rotation applies to the target application and its siblings sharing the same webhook, so a single call keeps every linked deployment in sync with the new secret. Until now, rotating a webhook secret meant creating a new application, which was cumbersome and error-prone. The new endpoint replaces that workflow with a single authenticated call. + +## How to use it + +You can call this endpoint through any [authenticated request to the Clever Cloud API](/doc/cli/#tokens), or through the [`clever curl` command](/doc/cli/#curl) of Clever Tools, which signs the request using the active user profile: + +```bash +clever curl -X POST https://api.clever-cloud.com/v2/organisations/{orgId}/applications/{appId}/github-webhook/secret +``` + +The response confirms the rotation. Any existing GitHub push event already in flight keeps working with the previous secret until GitHub picks up the new configuration; subsequent deliveries use the rotated secret automatically. + +Refer to the [v2 API reference](/api/v2) for the full request and response schema. From f02de5f59d938635c3998ed8f7427bfbcdbcb3cd Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 23 Apr 2026 09:43:30 +0200 Subject: [PATCH 014/214] changelog: Otoroshi 17.15 --- .../changelog/2026/04-23-otoroshi-17.15.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 content/changelog/2026/04-23-otoroshi-17.15.md diff --git a/content/changelog/2026/04-23-otoroshi-17.15.md b/content/changelog/2026/04-23-otoroshi-17.15.md new file mode 100644 index 000000000..c902e3555 --- /dev/null +++ b/content/changelog/2026/04-23-otoroshi-17.15.md @@ -0,0 +1,46 @@ +--- +title: Otoroshi 17.15 brings agents with tools and memory, OAuth2 token exchange, PluginPresets and requires Java 25 +description: Agents' built-in tools and persistent memory, new embedding and memory storage backends, Kreuzberg OCR support, OAuth2 token exchange plugin, PluginPresets and workflow auth modules +date: 2026-04-23 +tags: + - addons + - otoroshi +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Otoroshi v17.15.1](https://github.com/MAIF/otoroshi/releases/tag/v17.15.1) is available. It introduces an OAuth2 token exchange plugin implementing [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) for secure service-to-service API communication, a new `PluginPreset` kind to package and reuse plugin configurations, a `MandatoryConsumerPreset` plugin and a workflow-based authentication module. The `OAuth2Caller` plugin can now rely on an authentication module for the client credentials flow, and `$2b$` / `$2y$` Bcrypt hashes are now accepted. + +### A redesigned admin interface and manual + +The admin interface has been redesigned with refreshed themes, a new home page, a reworked API editor with sticky actions and form views capped at 1000 px to improve readability. The UI also now only displays entities a user is actually authorized to see. The full manual has been reworked as well, with ditaa schemas replaced by mermaid ones, and the OpenAPI specification and Swagger UI can be protected behind an optional access secret. + +### API management, data exporters and plugin development + +API management gains direct subscription to a plan and a new consistency service. The PostgreSQL data exporter now supports retention and plugin developers can rely on a reusable stateful clients internal API. Several fixes address a race condition in data exporters loading, legacy audit event layouts, body consumption handling and the `OAuth2Caller` password grant type. + +### LLM extension: agents, memory and Kreuzberg + +This release includes LLM extension [0.0.75](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.75), which significantly expands agent capabilities with built-in tools (`system_exec`, `file_read`, `file_write`, `http_call`), a per-session working memory, autonomous persistent memory on Redis and PostgreSQL and a new agent OpenAI proxy plugin. Embedding stores and persistent memories now support many backends (PostgreSQL, Redis, Elasticsearch, OpenSearch, Qdrant, Weaviate, Pinecone, ChromaDB), a semantic cache on Redis with a custom embedding model and MCP audit events. The Anthropic provider exposes version and beta settings, and [Kreuzberg](https://kreuzberg.dev) is now available as a content-to-markdown and OCR backend. + +### Java 25 is now required + +Otoroshi 17.15 requires Java 25 to load the Kreuzberg content-to-markdown and OCR backend. Starting with this release, all new Otoroshi add-ons are created with `CC_JAVA_VERSION=25`, and any update to Otoroshi 17.15 automatically switches the underlying Java application to Java 25. If you prefer to handle this manually, you can set `CC_JAVA_VERSION` to `25` on the Java application backing your Otoroshi add-on and rebuild it before updating Otoroshi itself. + +You can update through your add-on dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_OTOROSHI_VERSION` of the underlying Java application to `v17.15.1_1776891995` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever otoroshi version check yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId v17.15.1_1776891995 +``` + +- [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) From 8390eec5d8b4dc3b393509c7014c0b9e31dd2def Mon Sep 17 00:00:00 2001 From: Anis Nielsen Date: Tue, 5 May 2026 16:15:57 +0200 Subject: [PATCH 015/214] reference(env vars): rename VPN add-on env vars to CC_VPN_* --- .../reference-environment-variables.md | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/content/doc/reference/reference-environment-variables.md b/content/doc/reference/reference-environment-variables.md index 67860ea5b..e55b5132b 100644 --- a/content/doc/reference/reference-environment-variables.md +++ b/content/doc/reference/reference-environment-variables.md @@ -607,20 +607,23 @@ When your Python application doesn't use one of the supported backends, with `CC ### VPN The VPN add-on provides a fixed-ip outgoing node. This can be used to work -with services protected by ip address filtering. `VPN_ADDON_*` variables will +with services protected by ip address filtering. `CC_VPN_*` variables will be provided by Clever Cloud upon setup, the only configuration you have to provide is a list of CIDRs (eg. 1.2.3.0/24) for which you want the traffic to be routed through the exit node. +> [!NOTE] +> New setups use the `CC_VPN_*` names listed below. The previous `VPN_ADDON_*` names (and `VPN_TARGETS`) are deprecated and kept only for backward compatibility with existing setups. + | Name | Description | Default value | Read Only | | --------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------ | -------------------------- | | `CC_VPN_DNS_OVERRIDES` | Comma-separated list of DNS IP | | | -| `VPN_ADDON_CACRT` | Server CA certificate | | | -| `VPN_ADDON_CIPHER`| Cipher to use CIPHER, can be either {cipher_suite}:{hmac_alg} or only {cipher_suite} | DES-EDE3-CBC:SHA1 | | -| `VPN_ADDON_CRT` | Client certificate | | | -| `VPN_ADDON_DEVTYPE` | Kernel virtual interface kind to use ("tap" or "tun") | tap | | -| `VPN_ADDON_HOST` | Server host or IP address | | | -| `VPN_ADDON_KEY` | Client certificate private key | | | -| `VPN_ADDON_PORT` | Server port | | | -| `VPN_ADDON_TAKEY` | Pre-shared secret | | | -| `VPN_TARGETS` | Comma-separated list of CIDRs for which you want the traffic to be routed through the exit node | | | +| `CC_VPN_CACRT` | Server CA certificate | | | +| `CC_VPN_CIPHER`| Cipher to use CIPHER, can be either {cipher_suite}:{hmac_alg} or only {cipher_suite} | DES-EDE3-CBC:SHA1 | | +| `CC_VPN_CRT` | Client certificate | | | +| `CC_VPN_DEVTYPE` | Kernel virtual interface kind to use ("tap" or "tun") | tap | | +| `CC_VPN_HOST` | Server host or IP address | | | +| `CC_VPN_KEY` | Client certificate private key | | | +| `CC_VPN_PORT` | Server port | | | +| `CC_VPN_TAKEY` | Pre-shared secret | | | +| `CC_VPN_TARGETS` | Comma-separated list of CIDRs for which you want the traffic to be routed through the exit node | | | From d6e2168de2ad0e433507000cf340ff157bbdfe80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Nivolle?= Date: Wed, 6 May 2026 09:33:51 +0200 Subject: [PATCH 016/214] addons(elastic): add note about migrations --- content/doc/addons/elastic.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/doc/addons/elastic.md b/content/doc/addons/elastic.md index 0bcd0f0d3..42bb17741 100644 --- a/content/doc/addons/elastic.md +++ b/content/doc/addons/elastic.md @@ -197,3 +197,7 @@ Most settings are available for modifications and update from Kibana or by API, - Backups destination If you think your system might require some customization (like some plugins activation), contact Clever Cloud support to explain your use case and we will work with you to find a solution. + +## Migrations and upgrades + +When migrating or upgrading an Elasticsearch instance — especially when it's deployed as a cluster with multiple VMs and nodes — contact [our support team](https://console.clever-cloud.com/ticket-center-choice) beforehand to confirm feasibility and coordinate the operation without disruption. From 6c58dd51065cde42d3396779fc3c1f1b30798185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Nivolle?= Date: Tue, 5 May 2026 10:56:04 +0200 Subject: [PATCH 017/214] applications: add note about runtime extensions support --- content/doc/applications/_index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/doc/applications/_index.md b/content/doc/applications/_index.md index c1df96762..a80416d9d 100644 --- a/content/doc/applications/_index.md +++ b/content/doc/applications/_index.md @@ -58,6 +58,10 @@ Refer to the [Docker](docker) section of this documentation to know how to deplo If you are out of options, contact our support team and we'll come up with a solution with you. +## Custom Runtime Extensions + +Some runtimes can be extended with additional modules (such as extra PHP extensions). To request one, contact [our support team](https://console.clever-cloud.com/ticket-center-choice) with your use case so they can assess feasibility on the target operating system. + ## Environment Variables You can control deployments and set your application configuration with environment variables: From 4d38f4fab26972b726b48715dae888bb8066f25a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 09:56:26 +0200 Subject: [PATCH 018/214] applications: update options for unsupported runtimes --- content/doc/applications/_index.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/content/doc/applications/_index.md b/content/doc/applications/_index.md index a80416d9d..3a50c87c5 100644 --- a/content/doc/applications/_index.md +++ b/content/doc/applications/_index.md @@ -49,14 +49,11 @@ Find here specific instructions related to your application's language. {{< card link="/developers/doc/applications/v" title="V (Vlang)" icon="v" >}} {{< /cards >}} -## How To Deploy X if It Isn't Natively Supported +## Deploying a Non-native Runtime -If your favorite runtime is not available, you can deploy it on Clever Cloud by Dockerizing it and make it run in a Docker instance. -You will probably find a basic Docker file for your technology on the Docker hub. +If your runtime isn't available natively, the [Linux runtime](/developers/doc/applications/linux) with [Mise](https://mise.jdx.dev/) can run many languages and tools — see examples in [our GitHub examples and demos](https://github.com/CleverCloud/examples-and-demos). Otherwise, use the [Docker runtime](/developers/doc/applications/docker) with a Dockerfile from [Docker Hub](https://hub.docker.com/) or your own. -Refer to the [Docker](docker) section of this documentation to know how to deploy your Dockerized application. - -If you are out of options, contact our support team and we'll come up with a solution with you. +If none of these options fit, contact [our support team](https://console.clever-cloud.com/ticket-center-choice) to find a solution together. ## Custom Runtime Extensions From d1b1840e5f9e1dc0cc1278b0bcac49ac32794902 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:31:00 +0200 Subject: [PATCH 019/214] chore(icons): add npm in new Package managers section --- data/icons.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/icons.yaml b/data/icons.yaml index c5a439214..17af536d2 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -124,3 +124,6 @@ mdbook: kubernetes: + +# Package managers +npm: From e3fea1b45d83a5012efaa801077249f5bc80360a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:31:09 +0200 Subject: [PATCH 020/214] chore(icons): add pnpm --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index 17af536d2..6f656e7a1 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -127,3 +127,4 @@ kubernetes: +pnpm: From 7d21e482acf48139e646ac1776431b84256b97c3 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:31:21 +0200 Subject: [PATCH 021/214] chore(icons): add yarn --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index 6f656e7a1..3dc827ac0 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -128,3 +128,4 @@ kubernetes: pnpm: +yarn: From d245ca4149efbefd51a7d36f50e952cf00d8c583 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:31:44 +0200 Subject: [PATCH 022/214] chore(icons): add github in new Brands and services section --- data/icons.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/icons.yaml b/data/icons.yaml index 3dc827ac0..be496f5b9 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -129,3 +129,6 @@ kubernetes: pnpm: yarn: + +# Brands and services +github: From 354c86c6fa7bbe769eaf6ea9565b902992dbf5ee Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:31:55 +0200 Subject: [PATCH 023/214] chore(icons): add aws --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index be496f5b9..c401492e9 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -132,3 +132,4 @@ yarn: +aws: From 7730a0393953c8f84a958586bdef4d654246052c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:32:01 +0200 Subject: [PATCH 024/214] chore(icons): add minio --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index c401492e9..663263682 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -133,3 +133,4 @@ yarn: aws: +minio: From ef5910a673ef2fde4494216d202bb6c129d5a485 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:32:27 +0200 Subject: [PATCH 025/214] chore(icons): add apple in new Operating systems section --- data/icons.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/icons.yaml b/data/icons.yaml index 663263682..1dfd92343 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -134,3 +134,6 @@ yarn: aws: minio: + +# Operating systems +apple: From d1d21ba34d6db27de8af6bab7e2be104ef22fa59 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:32:34 +0200 Subject: [PATCH 026/214] chore(icons): add windows --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index 1dfd92343..d0db593dd 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -136,4 +136,5 @@ aws: # Operating systems +windows: apple: From cf93339bc674de65d05f5f3fd5175e7203ce6917 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:32:54 +0200 Subject: [PATCH 027/214] chore(icons): add json in new Data formats section --- data/icons.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/icons.yaml b/data/icons.yaml index d0db593dd..800525cda 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -138,3 +138,6 @@ minio: apple: + +# Data formats +json: From a220eef3ab8516a7699159864f052db3575a1820 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:33:01 +0200 Subject: [PATCH 028/214] chore(icons): add yaml --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index 800525cda..34a22050d 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -141,3 +141,4 @@ apple: +yaml: From 2550a391963fc1298f13aaf242d0992958ef3004 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:33:06 +0200 Subject: [PATCH 029/214] chore(icons): add toml --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index 34a22050d..498fd8c5c 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -142,3 +142,4 @@ apple: yaml: +toml: From 1021fd2eba99ea89a4f7f5ede6aa7bc0367b7dcf Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:33:22 +0200 Subject: [PATCH 030/214] chore(icons): add envelope in new UI utility section --- data/icons.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/icons.yaml b/data/icons.yaml index 498fd8c5c..b13ee5db3 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -139,6 +139,9 @@ minio: apple: +# UI utility +envelope: + # Data formats json: yaml: From 8db989df40fe9aff936cd9cc2db61ee5b63a5c23 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:56:21 +0200 Subject: [PATCH 031/214] chore(icons): add mistral in new AI assisstants section --- data/icons.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/icons.yaml b/data/icons.yaml index b13ee5db3..6a4d414d2 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -146,3 +146,6 @@ envelope: yaml: toml: + +# AI assistants +mistral: From 8fbda75d29abae6edc6648b2147ed5b4fb440fde Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:56:29 +0200 Subject: [PATCH 032/214] chore(icons): add perplexity --- data/icons.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/icons.yaml b/data/icons.yaml index 6a4d414d2..35ce9c087 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -149,3 +149,4 @@ toml: +perplexity: From 1a24f115e502798e6fb0ba17bb3bdd5a5197ccf9 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:35:09 +0200 Subject: [PATCH 033/214] fix: update tabs syntax, add icons --- content/doc/account/ssh-keys-management.md | 6 +++--- content/doc/addons/cellar.md | 24 +++++++++++----------- content/doc/administrate/log-management.md | 6 +++--- content/doc/ci-cd/custom-scripts.md | 6 +++--- content/doc/quickstart.md | 20 +++++++++--------- content/guides/astro.md | 14 ++++++------- content/guides/hugo-static-s3.md | 8 ++++---- 7 files changed, 42 insertions(+), 42 deletions(-) diff --git a/content/doc/account/ssh-keys-management.md b/content/doc/account/ssh-keys-management.md index e9e52830b..9954b2cc8 100644 --- a/content/doc/account/ssh-keys-management.md +++ b/content/doc/account/ssh-keys-management.md @@ -101,9 +101,9 @@ You can add any key already present in your GitHub account by clicking on the im You may already have an SSH key and so do not need to generate a new one. To check if you have one, follow these steps: -{{< tabs items="Linux and macOS,Windows" >}} +{{< tabs >}} -{{< tab >}} +{{< tab name="Linux and macOS" icon="linux" >}} 1. Whether you use macOS or Linux, open your Terminal application. 2. Run `cd ~/.ssh/` in your Terminal. @@ -115,7 +115,7 @@ If you can find them, you do not need to generate a new one, simply go to the fo {{< /tab >}} -{{< tab >}}1. If you don't have it, download [Git for Windows](https://git-for-windows.GitHub.io/) and install it. +{{< tab name="Windows" icon="windows" >}}1. If you don't have it, download [Git for Windows](https://git-for-windows.GitHub.io/) and install it. 2. Run **Git Bash** (from the *Start Menu* or from the *Explorer* with the contextual menu (right click)). 3. Run `cd ~/.ssh/` in your Terminal. 4. If the folder exists, run `ls` and check if a pair of key exists : *id_ed25519* and *id_ed25519.pub* or *id_rsa* and *id_rsa.pub*. We would recommend using *ed25519* keys. Smaller to copy and way stronger than 2048-bit RSA keys. If you can find them, you do not need to generate a new one, simply go to the following "Add your key on Clever Cloud" part! diff --git a/content/doc/addons/cellar.md b/content/doc/addons/cellar.md index 327b6bdaf..b25240667 100644 --- a/content/doc/addons/cellar.md +++ b/content/doc/addons/cellar.md @@ -118,9 +118,9 @@ This list isn't exhaustive. Feel free to [suggest other clients that you would l `s3cmd` allows you to manage your buckets using its commands, after [configuring it on your machine](#with-s3cmd) -{{< tabs items="Upload,List" >}} +{{< tabs >}} - {{< tab >}} + {{< tab name="Upload" >}} You can upload files (`--acl-public` makes the file publicly readable) with: ```bash @@ -130,7 +130,7 @@ This list isn't exhaustive. Feel free to [suggest other clients that you would l The file is then publicly available at `https://.cellar-c2.services.clever-cloud.com/image.jpg`. {{< /tab >}} - {{< tab >}} + {{< tab name="List" >}} You can list the files in your bucket, you should see the `image.png` file: ```bash @@ -156,9 +156,9 @@ Then, create a CNAME record on your domain pointing to `cellar-c2.services.cleve To use Cellar from your applications, you can use the [AWS SDK](https://aws.amazon.com/tools/#sdk) or any S3-compatible client. You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-cloud.com`). -{{< tabs items="Bun,Node.js,Java,Python,Ruby" >}} +{{< tabs >}} - {{< tab >}} + {{< tab name="Bun" icon="bun" >}} **Bun (native S3 client)** [Bun](https://bun.sh) includes a [native S3 client](https://bun.sh/docs/api/s3) with no external dependency. It works with any S3-compatible service, including Cellar. @@ -206,7 +206,7 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou {{< /tab >}} - {{< tab >}} + {{< tab name="Node.js" icon="node" >}} **Node.js** Using AWS SDK for JavaScript v3 (recommended): @@ -263,7 +263,7 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou {{< /tab >}} - {{< tab >}} + {{< tab name="Java" icon="java" >}} **Java** Import the AWS SDK S3 library. Maven uses the following dependency: @@ -345,7 +345,7 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou See the [AWS Java SDK code examples for S3](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/s3) for more example use cases. {{< /tab >}} -{{< tab >}} + {{< tab name="Python" icon="python" >}} **Python** This script uses boto3, the AWS SDK for Python. @@ -399,7 +399,7 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou {{< /tab >}} - {{< tab >}} + {{< tab name="Ruby" icon="ruby" >}} **Active Storage (Ruby On Rails)** [Active Storage](https://guides.rubyonrails.org/active_storage_overview.html) can manage various cloud storage services like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage. To use Cellar, @@ -684,9 +684,9 @@ For that reason, we do recommend you to enable versioning when creating a new bu > [!WARNING] > Versioning can quickly take up a lot of space since multiple version of an object are stored in the bucket. -{{< tabs items="MinIO, AWS CLI" >}} +{{< tabs >}} - {{< tab >}} + {{< tab name="MinIO" icon="minio" >}} To use [minIO](https://min.io/docs/minio/linux/reference/minio-mc.html#command-mc), you must create an alias. @@ -747,7 +747,7 @@ When versioning is enabled, the newly added object is automatically provided wit {{< /tab >}} - {{< tab >}} + {{< tab name="AWS CLI" icon="aws" >}} The following command assumes you have configured your AWS CLI and added an alias as shown earlier in the section [Creating a bucket with AWS CLI](/doc/addons/cellar/#with-aws-cli) diff --git a/content/doc/administrate/log-management.md b/content/doc/administrate/log-management.md index 63980ef74..da653c0e9 100644 --- a/content/doc/administrate/log-management.md +++ b/content/doc/administrate/log-management.md @@ -240,9 +240,9 @@ To export logs from an application or an add-on to [OVHcloud Logs Data Platform] On your terminal, use the following command: -{{< tabs items="Application,Add-on" >}} +{{< tabs >}} - {{< tab >}}**Exporting logs from an application**: + {{< tab name="Application" >}}**Exporting logs from an application**: ```shell clever drain create ovh-tcp tcp://:514 -app --sd-params="X-OVH-TOKEN=\"\"" @@ -256,7 +256,7 @@ On your terminal, use the following command: {{< /tab >}} - {{< tab >}}**Exporting logs from an add-on**: + {{< tab name="Add-on" >}}**Exporting logs from an add-on**: ```shell clever drain create ovh-tcp tcp://:514 -addon --sd-params="X-OVH-TOKEN=\"\"" diff --git a/content/doc/ci-cd/custom-scripts.md b/content/doc/ci-cd/custom-scripts.md index 23da63433..3e2ef919b 100644 --- a/content/doc/ci-cd/custom-scripts.md +++ b/content/doc/ci-cd/custom-scripts.md @@ -20,9 +20,9 @@ keywords: You can write your own pipeline to deploy from either GitHub or GitLab. Use [Clever Cloud CLI](https://github.com/CleverCloud/clever-tools) with either Docker or Node image. Place the following snippets at the top of your `.gitlab-ci.yml` file: -{{< tabs items="Docker image, Node image" >}} +{{< tabs >}} - {{< tab >}}**Docker image**: + {{< tab name="Docker image" icon="docker" >}}**Docker image**: ```yaml variables: @@ -35,7 +35,7 @@ image: {{< /tab >}} - {{< tab >}}**Node image**: + {{< tab name="Node image" icon="node" >}}**Node image**: ```yaml variables: diff --git a/content/doc/quickstart.md b/content/doc/quickstart.md index ba454ac59..cafd38ef7 100644 --- a/content/doc/quickstart.md +++ b/content/doc/quickstart.md @@ -32,14 +32,14 @@ aliases: The API of Clever Cloud uses OAuth 1 to perform authentication actions. There are two ways to sign up for Clever Cloud: **email** or **GitHub login**. -{{< tabs items="Email Auth, GitHub Auth" >}} +{{< tabs >}} - {{< tab >}} + {{< tab name="Email Auth" icon="envelope" >}} This kind of auth requires a valid and non-temporary disposable email, and a password having at least 6 characters. Do not forget to validate your email by clicking the link you will receive. {{< /tab >}} - {{< tab >}} + {{< tab name="GitHub Auth" icon="github" >}} The GitHub sign up allows you to create an account or link your existing one to GitHub, in one click. This process asks the following permissions: @@ -152,8 +152,8 @@ Enter the name and the description of your application. #### Choose How to Deploy -{{< tabs items="Git,GitHub, FTP" >}} - {{< tab >}} +{{< tabs >}} + {{< tab name="Git" icon="git" >}} *To deploy via Git, you need it installed on your machine. You can find more information on Git website: [git-scm.com](https://git-scm.com)* *Note:* during the deployment, the .git folder is automatically deleted to avoid security problems. If you need to know which version is used on the server please use the `COMMIT_ID` [environment variable](/doc/reference/reference-environment-variables/). @@ -186,7 +186,7 @@ git push :master {{< /tab >}} - {{< tab >}} + {{< tab name="GitHub" icon="github" >}} Once you have created your application with GitHub, each push on the `master` branch trigger a deployment. To deploy an other branch than `master`, go to the `information` panel of your application and select the default branch to use. ![GitHub deployment branch select](/images/github-deployment-branch.png "Github deployment branch select") @@ -200,7 +200,7 @@ git push :master However, if you set up an organisation, create the repository under the aegis of the organisation, and then add the collaborator, you have much more fine-grained control (including giving read-only access to a private repository). {{< /tab >}} - {{< tab >}} + {{< tab name="FTP" >}} You can deploy via FTP with PHP applications. To deploy via FTP, you need an FTP software installed on your machine. [Filezilla](https://filezilla-project.org/) is one of them. @@ -320,8 +320,8 @@ Clever Cloud provides multiple add-ons to work with your applications: **If your add-on:** -{{< tabs items="Doesn't exist yet,Already exists" >}} - {{< tab >}} +{{< tabs >}} + {{< tab name="Doesn't exist yet" >}} Here we will assume you want to create a new add-on and link it to your application. 1. Go to the [Clever Cloud Console](https://console.clever-cloud.com/). @@ -336,7 +336,7 @@ Clever Cloud provides multiple add-ons to work with your applications: The add-on will now be available in your organisation, and corresponding environment variables will be available for the applications linked to the add-on you just created. {{< /tab >}} - {{< tab >}} + {{< tab name="Already exists" >}} To link an already existing add-on with your application, just follow these steps: 1. Go in the organisation of your application. diff --git a/content/guides/astro.md b/content/guides/astro.md index 9b47cbb60..eb2f0cd5d 100644 --- a/content/guides/astro.md +++ b/content/guides/astro.md @@ -79,13 +79,13 @@ To deploy an Astro project with Server-Side Rendering (SSR), use a **Node.js** a Depending on your package manager, use the following environment variables: -{{< tabs items="npm,pnpm,yarn" >}} - {{< tab >}} +{{< tabs >}} + {{< tab name="npm" icon="npm" >}} ```shell CC_POST_BUILD_HOOK="npm run build" ``` {{< /tab >}} - {{< tab >}} + {{< tab name="pnpm" icon="pnpm" >}} ```shell CC_NODE_BUILD_TOOL="custom" CC_PRE_BUILD_HOOK="npm install -g pnpm && pnpm install" @@ -93,7 +93,7 @@ Depending on your package manager, use the following environment variables: CC_RUN_COMMAND="pnpm run preview" ``` {{< /tab >}} - {{< tab >}} + {{< tab name="yarn" icon="yarn" >}} ```shell CC_NODE_BUILD_TOOL="yarn" CC_PRE_BUILD_HOOK="yarn && yarn run astro telemetry disable && yarn build" @@ -109,8 +109,8 @@ Depending on your package manager, use the following environment variables: As you manage the server, ensure to configure your application to listen on port **8080** as required by Clever Cloud. Set your port and host in your `astro dev` script for development mode, and/or configure it directly for production: -{{< tabs items="development,production" >}} - {{< tab >}} +{{< tabs >}} + {{< tab name="development" >}} To quickly deploy on development mode: ```json {filename="package.json"} @@ -123,7 +123,7 @@ As you manage the server, ensure to configure your application to listen on port } ``` {{< /tab >}} - {{< tab >}} + {{< tab name="production" >}} When deploying for production: ```javascript {filename="astro.config.mjs"} diff --git a/content/guides/hugo-static-s3.md b/content/guides/hugo-static-s3.md index e9641c099..b67491610 100644 --- a/content/guides/hugo-static-s3.md +++ b/content/guides/hugo-static-s3.md @@ -105,9 +105,9 @@ In the folder of your website, you’ll find Hugo’s configuration file: Open it, then add the following (according to the programing language): -{{< tabs items="JSON,YAML,TOML" >}} +{{< tabs >}} - {{< tab >}}**JSON** + {{< tab name="JSON" icon="json" >}}**JSON** ```json {filename="hugo.json"} { @@ -125,7 +125,7 @@ Open it, then add the following (according to the programing language): {{< /tab >}} - {{< tab >}}**YAML** + {{< tab name="YAML" icon="yaml" >}}**YAML** ```json {filename="hugo.yaml"} deployment: @@ -137,7 +137,7 @@ Open it, then add the following (according to the programing language): {{< /tab >}} - {{< tab >}}**TOML** + {{< tab name="TOML" icon="toml" >}}**TOML** ```json {filename="hugo.toml"} [deployment] [[deployment.targets]] From 03621730d7a493cb9e903d3e7e6f050e86436e5a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:35:28 +0200 Subject: [PATCH 034/214] feat: enable contextMenu with AI services --- hugo.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hugo.yaml b/hugo.yaml index c56da2fcc..82ac18ff3 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -175,6 +175,21 @@ params: width: full tabs: sync: true + contextMenu: + enable: true + links: + - name: Open in ChatGPT + icon: chatgpt + url: "https://chatgpt.com/?hints=search&q=Read+this+Clever+Cloud+documentation+page+and+help+me+understand+and+apply+it+to+my+project%3A+{url}%0ARaw+Markdown+source%3A+{markdown_url}" + - name: Open in Claude + icon: claude + url: "https://claude.ai/new?q=Read+this+Clever+Cloud+documentation+page+and+help+me+understand+and+apply+it+to+my+project%3A+{url}%0ARaw+Markdown+source%3A+{markdown_url}" + - name: Open in Le Chat + icon: mistral + url: "https://chat.mistral.ai/chat?q=Read+this+Clever+Cloud+documentation+page+and+help+me+understand+and+apply+it+to+my+project%3A+{url}%0ARaw+Markdown+source%3A+{markdown_url}" + - name: Open in Perplexity + icon: perplexity + url: "https://www.perplexity.ai/search?q=Read+this+Clever+Cloud+documentation+page+and+help+me+understand+and+apply+it+to+my+project%3A+{url}%0ARaw+Markdown+source%3A+{markdown_url}" search: enable: true From ab7085ea8f7c9695f2d2f40c8ae399e85767c3b8 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:35:40 +0200 Subject: [PATCH 035/214] feat: enable imageZoom --- hugo.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hugo.yaml b/hugo.yaml index 82ac18ff3..412e91dde 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -191,6 +191,9 @@ params: icon: perplexity url: "https://www.perplexity.ai/search?q=Read+this+Clever+Cloud+documentation+page+and+help+me+understand+and+apply+it+to+my+project%3A+{url}%0ARaw+Markdown+source%3A+{markdown_url}" + imageZoom: + enable: true + search: enable: true type: flexsearch From 6962850e32088ca5bb69c0d3eca825c46a0e21dd Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:44:10 +0200 Subject: [PATCH 036/214] fix: language Hugo values deprecations --- hugo.yaml | 2 +- layouts/openapi/baseof.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hugo.yaml b/hugo.yaml index 412e91dde..34604903d 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -18,7 +18,7 @@ enableGitInfo: true enableInlineShortcodes: true hasCJKLanguage: true -languageCode: en-US +locale: en-US markup: highlight: diff --git a/layouts/openapi/baseof.html b/layouts/openapi/baseof.html index 7c04b27e6..52e1570da 100644 --- a/layouts/openapi/baseof.html +++ b/layouts/openapi/baseof.html @@ -1,5 +1,5 @@ - + From 87d3d1650c29157bb93229aff2d2c98138908b1d Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:44:43 +0200 Subject: [PATCH 037/214] fix: replace deprecated .Site.Data. with hugo.Data --- layouts/shortcodes/kubernetes_version.html | 2 +- layouts/shortcodes/runtime_version.html | 2 +- layouts/shortcodes/runtimes_versions.html | 2 +- layouts/shortcodes/software_versions_shared_dedicated.html | 2 +- layouts/shortcodes/tooltip.html | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/layouts/shortcodes/kubernetes_version.html b/layouts/shortcodes/kubernetes_version.html index 0da98df40..a564e2a68 100644 --- a/layouts/shortcodes/kubernetes_version.html +++ b/layouts/shortcodes/kubernetes_version.html @@ -1,3 +1,3 @@ {{- $key := .Get 0 -}} -{{- $value := index .Site.Data.kubernetes_versions $key -}} +{{- $value := index hugo.Data.kubernetes_versions $key -}} {{- if reflect.IsSlice $value -}}{{ delimit $value ", " }}{{- else -}}{{ $value }}{{- end -}} diff --git a/layouts/shortcodes/runtime_version.html b/layouts/shortcodes/runtime_version.html index 084945473..8ca5e4880 100644 --- a/layouts/shortcodes/runtime_version.html +++ b/layouts/shortcodes/runtime_version.html @@ -1 +1 @@ -{{ $software := .Get 0 }}{{ $key := or (.Get 1) "default" }}{{ with index .Site.Data.runtime_versions $software }}{{ with index . $key }}{{ index . 0 }}{{ end }}{{ end }} \ No newline at end of file +{{ $software := .Get 0 }}{{ $key := or (.Get 1) "default" }}{{ with index hugo.Data.runtime_versions $software }}{{ with index . $key }}{{ index . 0 }}{{ end }}{{ end }} \ No newline at end of file diff --git a/layouts/shortcodes/runtimes_versions.html b/layouts/shortcodes/runtimes_versions.html index beea16c45..5d5b163b7 100644 --- a/layouts/shortcodes/runtimes_versions.html +++ b/layouts/shortcodes/runtimes_versions.html @@ -1,5 +1,5 @@ {{ $software := or (.Get 0) }} -{{ $version_list := .Site.Data.runtime_versions }} +{{ $version_list := hugo.Data.runtime_versions }} {{ with index $version_list $software }} {{ $default := .default | default (slice) }} diff --git a/layouts/shortcodes/software_versions_shared_dedicated.html b/layouts/shortcodes/software_versions_shared_dedicated.html index 448e93013..cb2876c9b 100644 --- a/layouts/shortcodes/software_versions_shared_dedicated.html +++ b/layouts/shortcodes/software_versions_shared_dedicated.html @@ -1,5 +1,5 @@ {{ $software := or (.Get 0) }} -{{ $version_list := .Site.Data.software_versions_shared_dedicated }} +{{ $version_list := hugo.Data.software_versions_shared_dedicated }} {{ $software_versions := index $version_list $software }} {{ $has_dev := false }} diff --git a/layouts/shortcodes/tooltip.html b/layouts/shortcodes/tooltip.html index 807e5e763..b1e112d88 100644 --- a/layouts/shortcodes/tooltip.html +++ b/layouts/shortcodes/tooltip.html @@ -3,7 +3,7 @@ {{- errorf "missing tooltip title" -}} {{- end -}} {{ .Scratch.Set "title" $title }} -{{ $def := index .Site.Data.tooltips (.Scratch.Get "title") }} +{{ $def := index hugo.Data.tooltips (.Scratch.Get "title") }} {{- if not $def -}} {{- errorf "%s not in tooltips" $title -}} From 5efb0701876a7789564fda785e4c7eb50f28f728 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 10:41:29 +0200 Subject: [PATCH 038/214] chore: update Hextra theme to 0.12.3 --- go.mod | 2 +- go.sum | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 990a24e38..b63284dd5 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/CleverCloud/documentation go 1.26 -require github.com/imfing/hextra v0.12.2 // indirect +require github.com/imfing/hextra v0.12.3 // indirect diff --git a/go.sum b/go.sum index 72fad666f..afa868093 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,2 @@ -github.com/imfing/hextra v0.11.1 h1:8pTc4ReYbzGTHAnyiebmlT3ijFfIXiGu1r7tM/UGjFI= -github.com/imfing/hextra v0.11.1/go.mod h1:cEfel3lU/bSx7lTE/+uuR4GJaphyOyiwNR3PTqFTXpI= -github.com/imfing/hextra v0.12.0 h1:f6y35hW/WDJEcx9S0dOmbICOBxYE0PmP6IJFsTUgVyY= -github.com/imfing/hextra v0.12.0/go.mod h1:YAv8XRNSmcqjieFwI7fVQK1AoY2Do+45DO9HGqxSGu4= -github.com/imfing/hextra v0.12.1 h1:3t1n0bmJbDzSTVfht93UDcfF1BXMRjeFojA071ri2l8= -github.com/imfing/hextra v0.12.1/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= -github.com/imfing/hextra v0.12.2 h1:qa+cHQ1LC/7ys9EhRNnHrRBAHu83Tm8rhh1oWO2a7cc= -github.com/imfing/hextra v0.12.2/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= +github.com/imfing/hextra v0.12.3 h1:DZHY2rUWYteyzjlHi9r4n7Bb5e2Q+6LXe4C1Dqn0ZjM= +github.com/imfing/hextra v0.12.3/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= From 0fee7dbb1be51562fc102829c6efecd14b16fe40 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 13:46:37 +0200 Subject: [PATCH 039/214] chore: update Hugo version --- mise.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 mise.toml diff --git a/mise.toml b/mise.toml new file mode 100644 index 000000000..ab6cd305c --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +hugo-extended = "latest" From 63230e95dec5f134ab4545ef21875f4beaac53a8 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 17:56:36 +0200 Subject: [PATCH 040/214] changelog: Metabase 60 RAM requirements --- .../changelog/2026/05-06-metabase-60-ram.md | 23 +++++++++++++++++++ content/doc/addons/metabase.md | 6 ++--- 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 content/changelog/2026/05-06-metabase-60-ram.md diff --git a/content/changelog/2026/05-06-metabase-60-ram.md b/content/changelog/2026/05-06-metabase-60-ram.md new file mode 100644 index 000000000..cb720d63f --- /dev/null +++ b/content/changelog/2026/05-06-metabase-60-ram.md @@ -0,0 +1,23 @@ +--- +title: "Metabase 60 and RAM requirements" +description: New Metabase add-ons are deployed on a S Java instance, and existing add-ons will be upgraded to S when they'll move to 60. +date: 2026-05-06 +tags: + - addons + - metabase +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Starting with its `x.60` branch, [Metabase requires at least 2 GB of RAM](https://github.com/metabase/metabase/issues/72942) to run. The XS Java instance currently used by default for the Metabase add-on on Clever Cloud only provides 1 GB of RAM, which is no longer enough. To make sure your Metabase instance keeps deploying and running smoothly, we are adapting the default sizing: + +- Metabase add-ons created from today onwards use a **S Java instance** instead of XS +- When the update to version 60 will be offered, existing add-ons will **automatically be migrated to a S Java instance** during the upgrade + +If you want to keep an XS instance, stay on the `x.59` branch by setting `CC_METABASE_VERSION` to `0.59` (or `1.59` for the Enterprise Edition) on the underlying Java application. Keep in mind that staying on x.59 means you will not receive the new features shipped with x.60 and beyond; security patches and some fixes will still be provided as long as the 0.59 branch is maintained. We recommend moving to a S instance and the latest branch as soon as possible. + +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) +- [Pricing of Java instances on Clever Cloud](https://www.clever-cloud.com/pricing/) diff --git a/content/doc/addons/metabase.md b/content/doc/addons/metabase.md index 566a2d285..8aa66bbe0 100644 --- a/content/doc/addons/metabase.md +++ b/content/doc/addons/metabase.md @@ -120,12 +120,12 @@ When you create the Metabase add-on, Clever Cloud automatically deploys: ## Plan sizing -By default, Metabase on Clever Cloud uses small-size resources, i.e: +Metabase on Clever Cloud uses small-size resources, i.e: -- XS Java +- S Java - XXS Small Space PostgreSQL -They are dimensioned to suit a majority of needs. You can however manage and adjust them directly [in the Console](https://console.clever-cloud.com/). For example when you have multiple users loading large dashboards concurrently or if your instance experiences crashes due to Out of Memory (OOM) issues, you should use a larger flavor for the Java application or activate auto-scalability. The PostgreSQL database is not likely to be needed a larger plan, but should this happen you can migrate it using Clever Cloud's Console. +These defaults are dimensioned to suit a majority of needs. You can however manage and adjust them directly [in the Console](https://console.clever-cloud.com/). For example when you have multiple users loading large dashboards concurrently or if your instance experiences crashes due to Out of Memory (OOM) issues, you should use a larger flavor for the Java application or activate auto-scalability. The PostgreSQL database is not likely to be needed a larger plan, but should this happen you can migrate it using Clever Cloud's Console. ## Version management, Security and Updates From 090fc3ae1d2fd7ed1909cc7def7ff81e3a469b05 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 6 May 2026 18:51:27 +0200 Subject: [PATCH 041/214] changelog: Kubernetes 1.36 by default --- .../2026/05-05-kubernetes-1.36-default.md | 33 +++++++++++++++++++ content/doc/kubernetes/_index.md | 4 +-- data/kubernetes_versions.yml | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 content/changelog/2026/05-05-kubernetes-1.36-default.md diff --git a/content/changelog/2026/05-05-kubernetes-1.36-default.md b/content/changelog/2026/05-05-kubernetes-1.36-default.md new file mode 100644 index 000000000..d90a76944 --- /dev/null +++ b/content/changelog/2026/05-05-kubernetes-1.36-default.md @@ -0,0 +1,33 @@ +--- +title: Kubernetes 1.36 is now used by default +description: Kubernetes 1.36 is now the default version for new clusters on Clever Kubernetes Engine +date: 2026-05-05 +tags: + - kubernetes + - release +authors: + - name: Gilles Biannic + link: https://github.com/GillesBIANNIC + image: https://github.com/GillesBIANNIC.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Kubernetes 1.36 "Haru" is available on Clever Kubernetes Engine [since its release](/changelog/2026/04-24-kubernetes-1.36/). It's now the default version deployed when no `--cluster-version` is specified. New clusters will run on v1.36 unless you explicitly pick another supported one. + +You can still target v1.35 or v1.34 with `--cluster-version`, as long as they remain supported (n-2 from the current release, mirroring the official Kubernetes support policy): + +```bash +clever k8s create myCluster --cluster-version 1.35 +``` + +Existing clusters are not migrated automatically. To move one to v1.36, use [Clever Tools](/doc/cli/kubernetes/): + +```bash +clever k8s version update myClusterNameOrId 1.36 +``` + +- [Learn more about Kubernetes 1.36](/changelog/2026/04-24-kubernetes-1.36/) +- [Learn more about Kubernetes on Clever Cloud](/doc/kubernetes/) diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index 6a239be84..a4e44ee99 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -285,8 +285,8 @@ example-nodegroup 2 2 M Synced 2m kubectl get nodes NAME STATUS ROLES AGE VERSION -example-nodegroup-node0 Ready 6d17h v1.35.4 -example-nodegroup-node1 Ready 3d18h v1.35.4 +example-nodegroup-node0 Ready 6d17h v1.36.0 +example-nodegroup-node1 Ready 3d18h v1.36.0 ``` `DESIREDNODECOUNT` is the number of nodes you asked for, `CURRENTNODECOUNT` is the number of nodes currently in the node group. When creating a node group, `CURRENTNODECOUNT` starts at `0` and increases until it reaches `DESIREDNODECOUNT`. diff --git a/data/kubernetes_versions.yml b/data/kubernetes_versions.yml index b2e2d7cd6..7585f268f 100644 --- a/data/kubernetes_versions.yml +++ b/data/kubernetes_versions.yml @@ -3,7 +3,7 @@ # Update this file when a new Kubernetes version becomes available or when the # platform default rolls forward. current: "1.36" -default: "1.35" +default: "1.36" supported: - "1.36" - "1.35" From 4f158adeaafe449006c07db7851e7ee26040c4df Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 12 May 2026 10:17:28 +0200 Subject: [PATCH 042/214] changelog: Matomo 5.10 --- content/changelog/2026/05-04-matomo-5.10.md | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 content/changelog/2026/05-04-matomo-5.10.md diff --git a/content/changelog/2026/05-04-matomo-5.10.md b/content/changelog/2026/05-04-matomo-5.10.md new file mode 100644 index 000000000..2e31e53ea --- /dev/null +++ b/content/changelog/2026/05-04-matomo-5.10.md @@ -0,0 +1,25 @@ +--- +title: Matomo 5.10 is available with dark mode and a refreshed interface +description: Switch to a new dark mode, enjoy a modernized interface and redesigned selectors for websites, segments and dashboards +date: 2026-05-04 +tags: + - addons + - matomo +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Our [Matomo](https://matomo.org/) add-on has been updated to version `5.10.0` which is now used by default. This release introduces a new dark mode, available from your personal settings, to reduce brightness in low-light environments. The interface has also been modernized with updated widget styling, refreshed navigation bars, redesigned selectors for websites, segments or dashboards. + +This version also improves the Goals submenu ordering, lets actions use configured delimiters for flat report labels, prevents modern content tables from overflowing their card containers and keeps action buttons properly styled during focus states. + +You can deploy this release from our [Console](https://console.clever-cloud.com) or [Clever Tools](/doc/cli/). Existing customers' add-ons are already up-to-date. + +- [Learn more about Matomo 5.10](https://matomo.org/changelog/matomo-5-10-0/) +- [Learn more about Matomo on Clever Cloud](/doc/addons/matomo/) From e51d71fdf12744bbd9a82b3c56a6a4c10f8fbe87 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 12 May 2026 10:35:23 +0200 Subject: [PATCH 043/214] changelog: Clever Tools 4.10 --- .../changelog/2026/05-07-clever-tools-4.10.md | 42 +++++++++++++++++++ content/doc/reference/cli.md | 8 ++-- 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 content/changelog/2026/05-07-clever-tools-4.10.md diff --git a/content/changelog/2026/05-07-clever-tools-4.10.md b/content/changelog/2026/05-07-clever-tools-4.10.md new file mode 100644 index 000000000..247892a8e --- /dev/null +++ b/content/changelog/2026/05-07-clever-tools-4.10.md @@ -0,0 +1,42 @@ +--- +title: "Clever Tools 4.10: organisation-scoped add-on providers" +date: 2026-05-07 +description: Clever Tools 4.10 adds an --org option to filter add-on providers, regions and plans for a specific organisation +tags: + - clever-tools + - cli + - addons +authors: + - name: Hubert Sablonnière + link: https://github.com/hsablonniere + image: https://github.com/hsablonniere.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Clever Tools 4.10.0](https://github.com/CleverCloud/clever-tools/releases/tag/4.10.0) is available. This release makes add-on provider discovery organisation-aware, so you can see exactly which providers, regions and plans are available for a given organisation before creating a new add-on. + +## Organisation-scoped add-on providers + +The `clever addon providers` and `clever addon providers show` commands now accept an `--org` option. When set, results are filtered to match the providers, zones and plans exposed by the `addonproviders` API for that organisation, instead of the global catalogue. + +```bash +# List add-on providers available for a specific organisation +clever addon providers --org orga_xxx + +# Inspect a single provider, with org-specific plans and regions +clever addon providers show postgresql-addon --org orga_xxx +``` + +The `clever addon create` command also uses `--org` to validate the requested region. Without it, region availability checks were not organisation-aware, which could let creation calls through in regions not allowed for your organisation. With this fix, the CLI rejects the call up front when the target region is not available in the selected organisation. + +## How to upgrade + +To upgrade Clever Tools, [use your favourite package manager](/doc/cli/install/). For example with `npm`: + +```bash +npm update -g clever-tools +clever version +``` diff --git a/content/doc/reference/cli.md b/content/doc/reference/cli.md index 7122e2a47..d86e9b68a 100644 --- a/content/doc/reference/cli.md +++ b/content/doc/reference/cli.md @@ -422,7 +422,8 @@ clever addon providers [options] **Options** ``` --F, --format Output format (human, json) (default: human) +-F, --format Output format (human, json) (default: human) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` #### addon providers show @@ -438,12 +439,13 @@ clever addon providers show [options] **Arguments** ``` -addon-provider Add-on provider +addon-provider Add-on provider ``` **Options** ``` --F, --format Output format (human, json) (default: human) +-F, --format Output format (human, json) (default: human) +-o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` ### addon rename From 6f537931d4690d157d8baf78755c548672d1c08c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 12 May 2026 11:48:10 +0200 Subject: [PATCH 044/214] changelog: Keycloak 26.6 with per-realm IP filtering --- content/changelog/2026/05-12-keycloak-26.6.md | 71 +++++++++++++++++++ content/doc/addons/keycloak.md | 37 +++++++++- 2 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 content/changelog/2026/05-12-keycloak-26.6.md diff --git a/content/changelog/2026/05-12-keycloak-26.6.md b/content/changelog/2026/05-12-keycloak-26.6.md new file mode 100644 index 000000000..ccc9f9fe2 --- /dev/null +++ b/content/changelog/2026/05-12-keycloak-26.6.md @@ -0,0 +1,71 @@ +--- +title: Keycloak 26.6 with per-realm IP filtering +description: Keycloak 26.6 is available on Clever Cloud and adds IP filtering to restrict admin, public and SCIM endpoints per realm +date: 2026-05-12 +tags: + - addons + - keycloak +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[The release 26.6.1](https://github.com/keycloak/keycloak/releases/tag/26.6.1) of Keycloak is available on Clever Cloud, bringing bug fixes on top of the new features introduced in [Keycloak 26.6.0](https://github.com/keycloak/keycloak/releases/tag/26.6.0). This version graduates several features from preview and adds new ones: + +- Step-up authentication for SAML (preview) +- Zero-downtime patch releases supported, with rolling updates for minor versions +- New Groups scope for user membership changes and Vault SPI lookup for client secrets +- JWT Authorization Grant, Federated client authentication and Workflows promoted to supported +- Identity Brokering APIs V2 (preview), the successor to legacy Token Exchange V1 for retrieving external IdP tokens +- OAuth Client ID Metadata Document (experimental), enabling Keycloak as an authorization server for the Model Context Protocol +- New `KCRAW_` environment variable prefix to preserve literal values, dedicated HTTP access log file, configurable log file rotation, graceful HTTP shutdown + +## Organization groups + +[Organization groups](https://www.keycloak.org/2026/04/org-groups) give each organization its own isolated, nestable group hierarchy. Two organizations can now have a `/Engineering/Backend` group each without sharing members, attributes or identifiers, which removes the need to namespace groups across the realm. + +Identity providers can assign users to organization groups automatically through two new mappers: Hardcoded Group, which adds every brokered user to a specific group, and Advanced Claim to Group, which routes users based on the value of an external IdP claim. Group memberships appear in the `organization` claim of OIDC tokens and as attributes in SAML assertions, so applications can authorize on them without an extra round-trip. Full automation is available through the new endpoints under `/admin/realms//organizations/{orgId}/groups`. + +## SCIM Realm API (experimental) + +This release also introduces the [SCIM Realm API](https://www.keycloak.org/2026/04/scim-as-experimental-feature) (System for Cross-domain Identity Management) as an experimental feature, disabled by default. It exposes POST, GET, PATCH, PUT and DELETE operations for users and groups, the core user, enterprise user and group schemas, and SCIM filtering and pagination on search endpoints. Bulk operations, password management, sorting and custom schemas and attributes are not supported yet. Two security fixes ship in this release, addressing an [IDOR on the SCIM PUT endpoint](https://github.com/keycloak/keycloak/issues/46658) and an [authorization bypass on user group management](https://github.com/keycloak/keycloak/issues/47536). + +To expose `/realms//scim/*` endpoints, add `scim-api` to the `KC_FEATURES` environment variable of the Java application and rebuild it. `KC_FEATURES` is a build-time setting, so a simple restart is not enough. Once the rebuild completes, enable SCIM on each target realm from the Keycloak admin console (Realm Settings, *SCIM API Enabled* toggle) where the SCIM base URL is also displayed. + +## Per-realm IP filtering + +On top of upstream features, this version of the Keycloak add-on deployed on Clever Cloud extends its IP filtering capabilities. In addition to the existing in-realm authenticator flow, you can now restrict access to administration, public and SCIM endpoints on a per-realm basis through environment variables of the underlying Java application. Four families of variables control these filters, each accepting a comma-separated list of IP addresses: + +- `CC_KEYCLOAK_ADMIN_IPS_`: restricts `/admin//*` and `/admin/realms//*` for a given realm +- `CC_KEYCLOAK_PUBLIC_IPS_`: restricts `/realms//*` (login pages, user authentication, tokens) +- `CC_KEYCLOAK_SCIM_IPS_`: restricts `/realms//scim/*` provisioning endpoints +- `CC_KEYCLOAK_ADMIN_IPS`: global fallback for any `/admin/*` endpoint not covered by a per-realm admin rule + +The realm name in the variable suffix must match the realm name as it appears in URLs (case-sensitive). Per-realm filters take precedence over the global admin filter. Blocked requests receive an `HTTP 403` response. If no IP filtering variable is set, Keycloak keeps its standard public behavior. + +For example, to allow only two office IPs to reach the `master` realm admin console and a dedicated server to call SCIM on the `production` realm: + +```bash +CC_KEYCLOAK_ADMIN_IPS_master="203.0.113.10,203.0.113.11" +CC_KEYCLOAK_SCIM_IPS_production="198.51.100.42" +``` + +## Updating + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_KEYCLOAK_VERSION` of the underlying Java application to `26.6.1` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever keycloak version check yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId 26.6.1 +``` + +- [Learn more about IP filtering on the Keycloak add-on](/doc/addons/keycloak#ip-filtering) +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak) diff --git a/content/doc/addons/keycloak.md b/content/doc/addons/keycloak.md index c56e98be1..6bafd8f74 100644 --- a/content/doc/addons/keycloak.md +++ b/content/doc/addons/keycloak.md @@ -182,15 +182,46 @@ Uploading previously exported data in `realms/import` folder in the associated F Keycloak uses an [FSBucket](/doc/addons/fs-bucket) to install themes and plugins. To deploy a custom theme or custom plugin, simply download them into the respective `themes` or `providers` folder in your FSBucket. -## Add IP filtering in Keycloak for admin console +## IP filtering -Two specific authentication flows with an IP addresses based filter are especially created and affected as default to clients `security-admin-console` and `admin-cli`. To use them (do not forget to make the same on each realm you want to protect): +You can restrict who can reach your Keycloak instance with two complementary mechanisms: a per-endpoint filter that blocks traffic before authentication runs, and an in-realm authentication flow that filters at sign-in time. They can be used together. + +### Per-realm, on admin, public and SCIM endpoints + +Starting with version `26.6`, the Keycloak add-on can filter incoming requests based on the client's public IP, with separate rules for each realm and for each endpoint category (admin console and admin API, public endpoints, SCIM provisioning). Filtering is configured through environment variables of the underlying Java application. Each variable accepts a comma-separated list of IP addresses and blocked requests receive an `HTTP 403` response. + +| Variable | Scope | Protected paths | +|----------|-------|-----------------| +| `CC_KEYCLOAK_ADMIN_IPS_` | Admin endpoints of a given realm | `/admin//*`, `/admin/realms//*` | +| `CC_KEYCLOAK_PUBLIC_IPS_` | Public endpoints of a given realm (login, tokens, user authentication) | `/realms//*` | +| `CC_KEYCLOAK_SCIM_IPS_` | SCIM provisioning endpoints of a given realm (requires the `scim-api` feature, see below) | `/realms//scim/*` | +| `CC_KEYCLOAK_ADMIN_IPS` | Global fallback for admin endpoints not covered by a per-realm rule | `/admin/*` | + +The realm name in the variable suffix must match the realm name as it appears in URLs (case-sensitive). Per-realm rules take precedence over the global admin filter. If none of these variables is set, Keycloak keeps its standard public behavior. + +For example, to allow only two office IPs to reach the `master` realm admin console, restrict the `production` realm to your application servers and reserve its SCIM endpoints for your identity sync server: + +```bash +CC_KEYCLOAK_ADMIN_IPS_master="203.0.113.10,203.0.113.11" +CC_KEYCLOAK_PUBLIC_IPS_production="198.51.100.10,198.51.100.11" +CC_KEYCLOAK_SCIM_IPS_production="198.51.100.42" +CC_KEYCLOAK_ADMIN_IPS="203.0.113.10" +``` + +Filters compare the client IP to the literal values you provide, so use individual IP addresses rather than CIDR ranges. If you configure a custom HTTP path through `KC_HTTP_RELATIVE_PATH`, the prefix is automatically prepended to the protected paths. + +> [!NOTE] Enabling the SCIM endpoints +> SCIM was introduced in Keycloak `26.6` as an [experimental feature](https://www.keycloak.org/2026/04/scim-as-experimental-feature) and is disabled by default. The `CC_KEYCLOAK_SCIM_IPS_` filter only takes effect once SCIM is enabled. Add `scim-api` (comma-separated if you already have other entries) to the `KC_FEATURES` environment variable of the Java application and rebuild it — `KC_FEATURES` is a build-time setting, so a simple restart is not enough. Once the rebuild completes, enable SCIM on each target realm from the Keycloak admin console (Realm Settings, *SCIM API Enabled* toggle) where the SCIM base URL is also displayed. + +### At the realm authentication flow level + +Two specific authentication flows with an IP-address-based filter are created by default and assigned to the `security-admin-console` and `admin-cli` clients. They run at sign-in time, inside Keycloak. To use them (do this on each realm you want to protect): - Enable "PLEASE-OPEN.IT Authenticator IP Range" to "Required" - Click on the crank to access parameters - Set IPs with authorized access -Those flows could be affected to your own clients if you need. +You can also assign these flows to your own clients if needed. ## Grafana dashboard & Metrics From 76290a646c0a0e911ae4db1f42ab71f44d343402 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 12 May 2026 11:06:04 +0200 Subject: [PATCH 045/214] changelog: Metabase 60 released --- content/changelog/2026/05-12-metabase-60.md | 44 +++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 content/changelog/2026/05-12-metabase-60.md diff --git a/content/changelog/2026/05-12-metabase-60.md b/content/changelog/2026/05-12-metabase-60.md new file mode 100644 index 000000000..63a3187cf --- /dev/null +++ b/content/changelog/2026/05-12-metabase-60.md @@ -0,0 +1,44 @@ +--- +title: "Metabase 60 is available, with Metabot, MCP server and split panel charts" +description: Metabot, MCP server integration, Slack querying, metrics explorer, split panel charts, frozen columns, transform inspector and more +date: 2026-05-12 +tags: + - addons + - metabase +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +The `x.60` branch of Metabase is now available on Clever Cloud. It brings Metabot with "bring your own key" support for Anthropic models, an official MCP server to connect Claude, Cursor and other tools, and a Slack integration to query data directly from a channel. + +It also introduces a metrics explorer to compare multiple metrics side-by-side and surface trends across dimensions, split panel charts to display multiple series as faceted panels, frozen columns and rows in tables, a transform inspector to compare data before and after a transform runs, model-to-transform migration in a few clicks, OpenID Connect (OIDC) authentication, remote sync with GitLab and Bitbucket in addition to GitHub, and multiple enhancements and bug fixes. + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_METABASE_VERSION` of the underlying Java application to `0.60` or `1.60` for the enterprise edition (EE) and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever metabase version check yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId 0.60 +``` + +## New RAM requirements + +Starting with the `x.60` branch, [Metabase requires at least 2 GB of RAM](https://github.com/metabase/metabase/issues/72942) to run. Because the XS Java instance previously used by default only provides 1 GB of RAM, the default sizing has been adapted: new Metabase add-ons are deployed on a **S Java instance**, and existing add-ons are **automatically migrated to a S Java instance** when upgrading to version 60. + +This new branch is not yet the default if you use `community-latest`, we'll move to it in the next few weeks. To prepare for the automatic move to x.60, we recommend scaling the underlying Java application from XS (the current default) to a S instance now from the **Scalability** link in your Metabase add-on dashboard. + +If you want to keep an XS instance, stay on the `x.59` branch by setting `CC_METABASE_VERSION` to `0.59` (or `1.59` for the Enterprise Edition) on the underlying Java application. Keep in mind that staying on x.59 means you will not receive the new features shipped with x.60 and beyond; security patches and some fixes will still be provided as long as the 0.59 branch is maintained. We recommend moving to a S instance and the latest branch as soon as possible. + +- [Learn more about Metabase 60](https://www.metabase.com/releases/metabase-60) +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) +- [Pricing of Java instances on Clever Cloud](https://www.clever-cloud.com/pricing/) + +{{< youtube id="IrQeCFHHU3A" >}} From 0a6662b449acb5554e5d959d7ebbe30aa93e3ee2 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 12 May 2026 14:47:18 +0200 Subject: [PATCH 046/214] changelog: Materia KV GraphQL layer --- .../2026/05-11-materia-kv-graphql.md | 30 ++++ content/doc/addons/materia-kv.md | 170 ++++++++++++++++-- 2 files changed, 186 insertions(+), 14 deletions(-) create mode 100644 content/changelog/2026/05-11-materia-kv-graphql.md diff --git a/content/changelog/2026/05-11-materia-kv-graphql.md b/content/changelog/2026/05-11-materia-kv-graphql.md new file mode 100644 index 000000000..a15af9a89 --- /dev/null +++ b/content/changelog/2026/05-11-materia-kv-graphql.md @@ -0,0 +1,30 @@ +--- +title: "Materia KV: query your data through GraphQL" +description: Read your Materia KV add-on through a typed GraphQL endpoint, in addition to the Redis API. Same keyspace, same token, served from a shared per-region URL. +date: 2026-05-11 +tags: + - addons + - materia + - kv + - graphql +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Materia KV](/doc/addons/materia-kv/) gains a second compatibility layer: a **GraphQL** endpoint that reads from the same keyspace as the Redis API. Every key you write through a Redis/Valkey compatible client is immediately queryable through GraphQL, with no synchronization in between. Both interfaces authenticate with the same token (`$KV_TOKEN` / `$REDIS_PASSWORD`), passed as a bearer token. + +As Materia KV is a distributed cluster, the GraphQL endpoint is a single URL shared by every add-on in a given region. For Paris: + +``` +https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql +``` + +The schema exposes a single root type, `MateriaKvQuery`, with queries for strings, hashes and sets — single-key lookups, batched reads, pattern matching, and server-side set algebra (`setIntersection`, `setDifference`, `setUnion`). Introspection is enabled, so you can browse the full schema in the embedded **GraphiQL** playground (open the URL in a browser) or pull it as SDL with any introspection tool. + +The GraphQL layer is **read-only** for now — mutations aren't supported yet. Use the Redis API for writes. Have a look at [this end-to-end example](https://github.com/CleverCloud/kv-graphql-example) combining both layers: writes via the Redis API, reads via GraphQL. + +- [Learn more about the GraphQL compatibility layer](/doc/addons/materia-kv/#using-the-graphql-compatibility-layer) +- [Materia KV write via Redis API, read via GraphQL](https://github.com/CleverCloud/kv-graphql-example) diff --git a/content/doc/addons/materia-kv.md b/content/doc/addons/materia-kv.md index 0eb806de9..f9272c962 100644 --- a/content/doc/addons/materia-kv.md +++ b/content/doc/addons/materia-kv.md @@ -30,9 +30,14 @@ You don't have to configure leaders, followers: high availability is included, b ## Compatibility layers -We didn’t want this Materia KV to come at the cost of complex configuration, requiring the use of special clients and ORMs. That’s why we’ve developed its compatibility layers. To “talk” to it, you don’t need a special API or tools specific to Clever Cloud. You'll be able to use it with existing solutions for **DynamoDB, GraphQL or Redis**. The first available layer is compatible with Redis API (and its variants as Reddict or Valkey). +We didn’t want this Materia KV to come at the cost of complex configuration, requiring the use of special clients and ORMs. That’s why we’ve developed its compatibility layers: each one lets you talk to Materia KV through an existing protocol, with the clients, CLIs and ORMs you already use — no Clever Cloud-specific SDK required. -Thus, you can use a Materia KV add-on with any compatible client within your applications, `redis-cli` or alternatives such as [iredis](https://github.com/laixintao/iredis). You can also use it with graphical interface (GUI). We tested many of them with success: +Two layers are available: + +- [**Redis API**](#using-the-redis-api-compatible-layer) (and variants such as Redict and Valkey) — full read/write access, the primary way to interact with Materia KV. +- [**GraphQL**](#using-the-graphql-compatibility-layer) — a typed, read-oriented view of the same keyspace, served from a standard GraphQL endpoint. + +Both layers operate on the same underlying data, so any key written through the Redis API is immediately visible through GraphQL. For the Redis API layer specifically, you can use `redis-cli`, `valkey-cli` or alternatives such as [iredis](https://github.com/laixintao/iredis), as well as graphical clients we've tested successfully: - [Another Redis Desktop Client](https://goanother.com/) - [PX3 Redis UI](https://github.com/patrikx3/redis-ui) @@ -44,7 +49,7 @@ Thus, you can use a Materia KV add-on with any compatible client within your app You can create a Materia KV add-on as simply as any other Clever Cloud service in the Console, [following this link](https://console.clever-cloud.com/users/me/addons/new). Select the plan (free during Beta testing phase), an application to link to (or none), give it a name, and you'll get access to its dashboard giving you connection details. Environment variables shared with a linked application are listed in the `Service dependencies` section. -We included them with the `REDIS_` format. Thus, you can just try to replace a Redis instance by Materia KV. It's as simple as linking the new add-on, unlinking the old one and restarting your application! (Check commands you'll need first). +We included them with the `REDIS_` format. Thus, you can just try to replace a Redis or Valkey instance by Materia KV. It's as simple as linking the new add-on, unlinking the old one and restarting your application! (Check commands you'll need first). You can also use clever tools to create a Materia KV add-on and set environment variables to test it with a `PING` command: @@ -84,7 +89,7 @@ You can also deploy Materia KV add-ons with [Terraform provider](https://registr ### Environment variables and CLI usage -To connect to a Materia KV add-on, you need 3 parameters: the host, the port and a ([biscuit](https://biscuitsec.org) based) token. You can set these parameters as environment variables by doing `source <(clever addon env addon ADDON_ID -F shell)`. The variables set are: +To connect to a Materia KV add-on, you need 3 parameters: the host, the port and a token. You can set these parameters as environment variables by doing `source <(clever addon env addon ADDON_ID -F shell)`. The variables set are: * `$KV_HOST` and its alias `$REDIS_HOST` * `$KV_PORT` and its alias `$REDIS_PORT` @@ -119,15 +124,6 @@ We're exploring how [Clever Tools](https://github.com/CleverCloud/clever-tools/) * [Learn more about Clever KV](/doc/cli/kv-stores/) -### Demos and examples - -We've prepared a few examples to help you get started with Materia KV: - -* [Materia KV Go client](https://github.com/CleverCloud/mkv-go-cli) -* [Materia KV raw TCP V demo](https://github.com/CleverCloud/mkv-raw-tcp-v) -* [Materia KV raw TCP Ruby demo](https://github.com/CleverCloud/mkv-raw-tcp-ruby) -* [Materia KV PHP sessions with TTL demo](https://github.com/CleverCloud/php-sessions-kv-example) - ### Supported types and commands Supported value types are: @@ -141,7 +137,7 @@ Find below the list of currently supported commands: |
Commands
| Description | | ------- | ----------- | | `APPEND` | If `key` already exists and is a string, this command appends the value at the end of the string. If `key` doesn't exist it is created and set as an empty string, so `APPEND` will be similar to `SET` in this special case. | -| `AUTH` | Authenticate the current connection using the biscuit token as `password`. | +| `AUTH` | Authenticate the current connection using the token as `password`. | | `CLIENT ID` | Returns the `ID` of the current connection. A connection ID has is never repeated and is monotonically incremental. | | `COMMAND` | Return an array with details about every supported command. | | `COMMAND COUNT` | Return the number of supported commands. | @@ -262,3 +258,149 @@ OK - `JSON.SET` can't create new fields in existing documents - Nested path creation is not supported (e.g., `$.new.child.field`) - Keys in your JSON must not contains characters like `..`, `*`, `[?(` + +## Using the GraphQL compatibility layer + +In addition to the Redis API, Materia KV exposes a **GraphQL** endpoint. It reads from the same keyspace as the Redis API layer — every key you write through the Redis API is immediately queryable through GraphQL, with no synchronization layer in between. Both interfaces authenticate with the same token, and that token scopes each request to your add-on's data on the shared cluster. + +The GraphQL layer is **read-only** today — mutations aren't supported yet. Use the Redis API for writes. + +### Endpoint and authentication + +Materia KV is a distributed cluster: the GraphQL endpoint is a single URL shared by every add-on in a given region, over HTTPS on the standard port. For the Paris region, it is: + +``` +https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql +``` + +Authentication uses the same token as the Redis API (`$KV_TOKEN` / `$REDIS_PASSWORD`), passed as a bearer token: + +```bash +curl -X POST "https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql" \ + -H "Authorization: Bearer $KV_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"query":"{ __typename }"}' +``` + +Missing or invalid credentials return **HTTP 401** with the error in the GraphQL `errors` array. This endpoint returns **HTTP 200** for all other errors (wrong-type reads, unknown fields, mutation requests, validation errors), with the details in `errors`. + +### GraphiQL playground + +Opening the GraphQL URL in a browser (plain `GET`) serves an embedded **GraphiQL** playground: an in-browser IDE with autocompletion, query history and a documentation explorer. Every request — including the schema introspection that powers autocomplete and the docs explorer — needs your token, otherwise GraphiQL displays `Error fetching schema`. + +**Initial setup, once per browser session:** + +1. At the **bottom of the query panel**, click the **Headers** tab (next to `Variables`). +2. Paste your token as a JSON object: + + ```json + { + "Authorization": "Bearer " + } + ``` + +3. Click the **Re-fetch GraphQL schema** icon at the bottom of the **left sidebar** (the circular-arrow icon, keyboard shortcut `Ctrl` + `Shift` + `R`). Introspection runs with your header, and the full schema becomes browsable. + +Once the header is set, the **Docs Explorer** (book icon, also in the left sidebar) shows the full type tree: every query, every argument, every return type, with the descriptions baked into the schema. + +### Fetching the schema + +Introspection is enabled, so you can pull the schema directly from the endpoint in three common ways. + +1. **Browse it in GraphiQL** — follow the setup above (headers + re-fetch), then click the Docs Explorer icon in the left sidebar. + +2. **Raw JSON introspection via `curl`** — useful for scripts and CI: + + ```bash + curl -X POST "https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql" \ + -H "Authorization: Bearer $KV_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"query":"{ __schema { queryType { name fields { name } } types { name kind } } }"}' + ``` + +3. **Download as SDL** (the classic `.graphql` schema file) using any introspection tool, such as `get-graphql-schema`: + + ```bash + npx -y get-graphql-schema \ + -h "Authorization=Bearer $KV_TOKEN" \ + "https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql" > schema.graphql + ``` + +The resulting file plugs straight into code generators, IDE plugins, and schema-aware editors. + +### Queries + +The schema exposes a single root type, `MateriaKvQuery`, with queries for strings, hashes and sets — including single-key lookups, batched reads, pattern matching and server-side set algebra (`setIntersection`, `setDifference`, `setUnion`). Fetch the full list of queries and their signatures through introspection (see [Fetching the schema](#fetching-the-schema) above) or browse them in the GraphiQL Docs Explorer. + +Single-key queries (`string`, `hash`, `hashField`, `getSetMembers`) return a **nullable** object — `null` when the key doesn't exist. Pattern and batch queries return **non-null lists** (possibly empty). + +### Query examples + +Fetch a single string, including its expiration: + +```graphql +query ReadSession($key: String!) { + string(key: $key) { + key + value + expireAt + } +} +``` + +Read a structured record in one round-trip: + +```graphql +query GetUser($key: String!) { + hash(key: $key) { + key + fields { name value } + } +} +``` + +Combine several reads into one request using [aliases](https://graphql.org/learn/queries/#aliases): + +```graphql +query Dashboard { + admins: getSetMembers(key: "group:admins") { members } + active: getSetMembers(key: "group:active") { members } + overlap: setIntersection(keys: ["group:admins", "group:active"]) +} +``` + +Always pass user input through **GraphQL variables** rather than string interpolation: the server type-checks every variable, which reduces injection risk and avoids query string interpolation issues: + +```bash +curl -X POST "https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql" \ + -H "Authorization: Bearer $KV_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "query": "query($k:String!){ string(key:$k){ key value } }", + "variables": { "k": "session:xyz" } + }' +``` + +### JSON values read through GraphQL + +JSON documents written via `JSON.SET` are stored on top of strings. The schema has no dedicated GraphQL type for JSON: read the document back through `string(key)` as the serialized payload and parse it client-side. Partial JSON paths (`JSON.GET key $.field`) are only available through the Redis API. + +### Current behaviors and limitations + +- Queries on a key of the wrong type (e.g. `hash(key)` on a string key) return a GraphQL error — `Database operation failed: Operation against a key holding the wrong kind of value` — rather than `null`. Make sure your query type matches the Redis type at the same key. +- `stringsByPattern(pattern)` rejects a call where the pattern matches **more than 100 keys** with an error (`Database operation failed: Max batch size exceeded: N > 100`) — results are not silently truncated. Narrow the pattern (or walk the keyspace through several tighter prefixes) when the match count is larger. +- `strings(keys: [...])` has the same hard limit: **at most 100 keys** per call. Chunk larger batches on the client side. +- Pattern-based queries (`hashesByPattern`, `setsByPattern`, `hashFieldsByPattern`) are not paginated: each call returns its full result set in one response. Keep patterns focused to avoid oversized payloads. +- Glob patterns follow the Redis `KEYS`/`SCAN` syntax (`*`, `?`, `[abc]`). +- The `expireAt` field is an absolute instant (ISO 8601), not a remaining TTL — parse it as an ISO 8601 timestamp in your client, then subtract the current time to compute a countdown. +- Authentication errors return HTTP 401 with the message in the `errors` array. Every other GraphQL error (wrong-type reads, mutation requests, validation errors) returns HTTP 200 with `errors` populated. + +## Demos and examples + +We've prepared a few examples to help you get started with Materia KV: + +* [Materia KV Go client](https://github.com/CleverCloud/mkv-go-cli) +* [Materia KV raw TCP V demo](https://github.com/CleverCloud/mkv-raw-tcp-v) +* [Materia KV raw TCP Ruby demo](https://github.com/CleverCloud/mkv-raw-tcp-ruby) +* [Materia KV PHP sessions with TTL demo](https://github.com/CleverCloud/php-sessions-kv-example) +* [Materia KV write via Redis API, read via GraphQL](https://github.com/CleverCloud/kv-graphql-example) From cc1b6cdd0f08b7424ba769a4f3cd04dd98c7ff2d Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 12 May 2026 16:02:04 +0200 Subject: [PATCH 047/214] changelog: kernel 7.0.6 --- .../2026/05-12-linux-kernel-7.0.6.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 content/changelog/2026/05-12-linux-kernel-7.0.6.md diff --git a/content/changelog/2026/05-12-linux-kernel-7.0.6.md b/content/changelog/2026/05-12-linux-kernel-7.0.6.md new file mode 100644 index 000000000..e3538fe68 --- /dev/null +++ b/content/changelog/2026/05-12-linux-kernel-7.0.6.md @@ -0,0 +1,21 @@ +--- +title: "Linux kernel 7.0.6 available" +description: The Linux kernel can now be updated independently from the rest of our images. Restart your application to run on 7.0.6. +date: 2026-05-12 +tags: + - images + - update + - kernel +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Over the last few weeks, we worked on decoupling the Linux kernel from the rest of our runtime images. This work is now complete: kernel upgrades no longer require a full image refresh and can roll out on their own cadence, which means faster delivery of security fixes and hardware support improvements without touching the language runtimes, build tools, or system libraries shipped in each image. + +As a first step on this new pipeline, **Linux 7.0.6** is now available. To run your application on this kernel, restart it from the [Clever Cloud Console](https://console.clever-cloud.com) or with `clever restart`. Newly deployed applications pick up the new kernel automatically. + +- [What's new in Linux 7.0](https://kernelnewbies.org/Linux_7.0) +- [Linux 7.0.6 changelog](https://cdn.kernel.org/pub/linux/kernel/v7.x/ChangeLog-7.0.6) From 7be5b1078487314f0bad78ec69ab77a31d3f872a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 12 May 2026 19:53:19 +0200 Subject: [PATCH 048/214] changelog: Cellar Explorer & NG in Console --- .../2026/05-12-cellar-dashboard-explorer.md | 27 +++++++++++++++++++ .../2026/05-12-network-groups-console.md | 26 ++++++++++++++++++ content/doc/addons/cellar.md | 10 ++++++- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 content/changelog/2026/05-12-cellar-dashboard-explorer.md create mode 100644 content/changelog/2026/05-12-network-groups-console.md diff --git a/content/changelog/2026/05-12-cellar-dashboard-explorer.md b/content/changelog/2026/05-12-cellar-dashboard-explorer.md new file mode 100644 index 000000000..76c2fd4ae --- /dev/null +++ b/content/changelog/2026/05-12-cellar-dashboard-explorer.md @@ -0,0 +1,27 @@ +--- +title: "Cellar: new dashboard and Cellar Explorer" +description: A new Cellar add-on dashboard surfaces access information, consumption, the s3cfg configuration file, and the Cellar Explorer to browse buckets and objects directly from the Console. +date: 2026-05-12 +tags: + - addons + - cellar + - console +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Cellar](/doc/addons/cellar/), our S3-compatible object storage service, gets a new dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). It puts the information you need to connect and operate your add-on within reach: access key ID and secret, endpoint, region, current storage and bandwidth consumption, plan and billing details. The pre-filled `s3cfg` configuration file is one click away, ready to drop into your home directory and use with `s3cmd` or any compatible tool. + +## Cellar Explorer + +The new dashboard also opens the door to the **Cellar Explorer**, a browser-side file manager for your Cellar buckets, built in the spirit of the [KV Explorer](/doc/addons/materia-kv/#clever-cloud-kv-explorer) we shipped for Materia KV and Redis® add-ons. It lists every bucket of the add-on, lets you navigate objects as folders, and supports the operations you reach for most often: download an object, upload one or several files, copy a public URL, inspect object metadata, all without leaving the Console or installing a third-party S3 client. + +Under the hood, the Cellar Explorer relies on the new Cellar APIs we've been rolling out, which also power upcoming features around versioning and usage reporting. Expect the tool to keep evolving over the coming months, with more operations exposed directly in the interface. + +The Cellar Explorer is available in Beta for every Cellar add-on. Share your feedback and feature requests on our [GitHub Community](https://github.com/CleverCloud/Community/discussions/155). + +- [Learn more about Cellar on Clever Cloud](/doc/addons/cellar/) +- [Share your feedback on the Cellar Explorer](https://github.com/CleverCloud/Community/discussions/155) diff --git a/content/changelog/2026/05-12-network-groups-console.md b/content/changelog/2026/05-12-network-groups-console.md new file mode 100644 index 000000000..ecad87f0f --- /dev/null +++ b/content/changelog/2026/05-12-network-groups-console.md @@ -0,0 +1,26 @@ +--- +title: "Network Groups are now available in the Console" +description: Create, browse and delete Network Groups directly from the Clever Cloud Console, link applications and add-ons, inspect members and peers without leaving the browser. +date: 2026-05-12 +tags: + - console + - network-groups + - wireguard +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Network Groups](/doc/develop/network-groups/) — our [WireGuard](https://www.wireguard.com/)-based private networks between Clever Cloud resources — are now first-class citizens in the [Clever Cloud Console](https://console.clever-cloud.com). Until now, creating and managing them required the [public API](/api/v4/#network-groups) or [Clever Tools](/doc/cli/network-groups/). The same operations are now available directly in the Console. + +From the Network Groups section of your organisation, you can create a new group with its label, description and tags, browse the groups already in place, and delete the ones you don't need anymore. Opening a group shows its CIDR, the members linked to it (applications, add-ons or external resources) and the peers currently connected, with domain name and IP inside the private network. + +Linking resources is also done from the Console: pick an application or an add-on from your organisation and add it as a member of the group in a few clicks. Removing a member works the same way, and the change is reflected immediately on the peers list as instances reconnect to the network. + +This rounds out the Network Groups experience across all our interfaces — API, CLI, and now the Console — so you can pick the one that fits your workflow. Share your feedback and feature requests on our [GitHub Community](https://github.com/CleverCloud/Community/discussions/156). + +- [Learn more about Network Groups](/doc/develop/network-groups/) +- [How to use Network Groups from Clever Tools](/doc/cli/network-groups/) +- [Share your feedback on Network Groups in the Console](https://github.com/CleverCloud/Community/discussions/156) diff --git a/content/doc/addons/cellar.md b/content/doc/addons/cellar.md index b25240667..092b36ec1 100644 --- a/content/doc/addons/cellar.md +++ b/content/doc/addons/cellar.md @@ -101,9 +101,17 @@ alias aws="aws --endpoint-url https://cellar-c2.services.clever-cloud.com" There are several ways to manage your buckets, find in this section a list of options. +### Cellar Explorer + +Browse your buckets, upload, download and inspect objects directly from the [Clever Cloud Console](https://console.clever-cloud.com) with the **Cellar Explorer** tool, part of the all-included Clever Cloud experience. + +From the add-on dashboard, the Cellar Explorer lists every bucket of the add-on and lets you navigate objects as folders. You can upload or download an object, copy its public URL and inspect its metadata without installing a third-party S3 client. The tool relies on the new Cellar APIs and will keep gaining features over the coming months. + +Cellar Explorer is in Beta testing phase. Share your feedback and feature requests on our [GitHub Community](https://github.com/CleverCloud/Community/discussions/155). + ### Using S3 clients -Some clients allows you to upload files, list them, delete them, etc, like: +Some clients allow you to upload files, list them, delete them, etc, like: - [Cyberduck](https://cyberduck.io) - [Filestash](https://www.filestash.app/) From 988ffd4d9b6927390dce0c129d0d047d9f079d96 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 19 May 2026 18:32:50 +0200 Subject: [PATCH 049/214] fix: OAuth2 Proxy branding --- content/changelog/2026/03-25-images-update.md | 2 +- content/changelog/2026/04-07-images-update.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content/changelog/2026/03-25-images-update.md b/content/changelog/2026/03-25-images-update.md index 4b7d41bea..353d5795d 100644 --- a/content/changelog/2026/03-25-images-update.md +++ b/content/changelog/2026/03-25-images-update.md @@ -22,7 +22,7 @@ We updated all our images, except PHP. Deployment is in progress for all our use * cURL 8.19.0 * FFmpeg 8.1 * Ghostscript 10.07 - * OAuth2Proxy 7.14.3 + * OAuth2 Proxy 7.14.3 * Poppler 26.03 * SQLite 3.52.0 * Varnish 8.0.1 diff --git a/content/changelog/2026/04-07-images-update.md b/content/changelog/2026/04-07-images-update.md index 9124590c8..16827c560 100644 --- a/content/changelog/2026/04-07-images-update.md +++ b/content/changelog/2026/04-07-images-update.md @@ -17,7 +17,7 @@ We updated all our images, except PHP. Deployment is in progress for all our use * **Common:** * Linux kernel 6.19.11 * nginx 1.28.3 - * OAuth2Proxy 7.15.1 + * OAuth2 Proxy 7.15.1 * Tailscale 1.96.3 * **Python:** * uv 0.11.2 From d3915fd43454bf04f6e2b5d4b5a70482ec2d596c Mon Sep 17 00:00:00 2001 From: Corentin BARAULT Date: Mon, 11 May 2026 15:25:55 +0200 Subject: [PATCH 050/214] addons(postgresql): customized backups mention and pgbackrest --- content/doc/addons/postgresql.md | 5 +++++ shared/db-backup.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/content/doc/addons/postgresql.md b/content/doc/addons/postgresql.md index e24e4d387..a2fbb7945 100644 --- a/content/doc/addons/postgresql.md +++ b/content/doc/addons/postgresql.md @@ -30,6 +30,11 @@ PostgreSQL is an object-relational database management system (ORDBMS) with an e {{% content "db-backup" %}} +## Point In Time Recovery + +The PostgreSQL add-on supports pgBackRest, enabling incremental backups and point-in-time data restoration. +It's not installed by default on a PostgreSQL add-on, if you need it, you can contact [Clever Cloud Support](https://console.clever-cloud.com/ticket-center-choice). + ## Migrating from an old database Some applications require a non-empty database to run properly. If you want to import your **SQL** dump, you can use several methods: diff --git a/shared/db-backup.md b/shared/db-backup.md index 367e169de..fd10fc0d4 100644 --- a/shared/db-backup.md +++ b/shared/db-backup.md @@ -1,5 +1,5 @@ ## Database Daily Backup and Retention -By default, Clever Cloud performs a free backup every day, with a retention of seven days. Retention and frequency can be customized for Premium customers. +By default, Clever Cloud performs a free backup every day, with a retention of seven days. Retention and frequency can be customized by contacting [Clever Cloud Support](https://console.clever-cloud.com/ticket-center-choice). Each backup can be found in the add-on dashboard in the web console, along with the credentials. From a0b3642de10cae5bac870e5cc27dd315d34b458c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 19 May 2026 18:43:08 +0200 Subject: [PATCH 051/214] changelog: images updates, 2026W21 --- content/changelog/2026/05-22-images-update.md | 72 +++++++++++++++++++ content/doc/applications/frankenphp.md | 2 +- .../applications/scala/play-framework-1.md | 3 +- content/doc/applications/static.md | 2 +- data/runtime_versions.yml | 2 +- 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 content/changelog/2026/05-22-images-update.md diff --git a/content/changelog/2026/05-22-images-update.md b/content/changelog/2026/05-22-images-update.md new file mode 100644 index 000000000..889bfb504 --- /dev/null +++ b/content/changelog/2026/05-22-images-update.md @@ -0,0 +1,72 @@ +--- +title: "Images update: PHP 8.5.6, Java security patches, Python 3.14.5, Hugo 0.161" +description: All images updated with security patches across every runtime, PHP included. Hugo support narrows to 0.160 and later from June 15th. +date: 2026-05-22 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images. Deployment is in progress for all our users. + +* **Common:** + * Apache 2.4.67 + * Chromium 148.0.7778.167 + * Clever Tools 4.10.0 + * cURL 8.20.0 + * Mise 2026.5.3 + * NGINX 1.30.1 + * OAuth2 Proxy 7.15.2 + * Redis 8.6.3 + * SQLite 3.53.1 +* **FrankenPHP:** + * Update to 1.12.3 (For `CC_PHP_VERSION=8.5`) +* **Go:** + * Go 1.26.3 +* **Java:** + * Update to 1.8.0.492_p09 + * Update to 11.0.31_p11 + * Update to 17.0.19_p10 + * Update to 21.0.11_p10 + * Update to 25.0.3_p9 + * Gradle 8.14.5 + * Gradle 9.5.1 +* **Node.js & Bun:** + * Bun 1.3.14 +* **PHP:** + * Update to 8.2.31 + * Update to 8.3.31 + * Update to 8.4.21 + * Update to 8.5.6 + * Composer 2.2.27 + * Composer 2.9.7 +* **Python:** + * Update to 3.14.5 + * uv 0.11.15 +* **Ruby:** + * Update to 4.0.5 +* **Scala:** + * Play Framework 1.2.7.2 + * Play Framework 1.11.0 +* **Static:** + * Hugo 0.152.2 + * Hugo 0.159.2 + * Hugo 0.160.1 + * Hugo 0.161.1 + +## Linux Kernel + +Kernel is [now updated independently](/changelog/2026/05-12-linux-kernel-7.0.6). Current version is 7.0.9. + +## Fixes for Java, Node.js & Bun, PHP + +This image includes multiple fixes for bug we encountered for Java applications using Maven, PHP applications using `apcu` or `zip` extensions, Node.js 25/26 applications using `pnpm` or `yarn`. + +## Hugo version update + +Starting June 15th, we will only support Hugo 0.160 release and later. If you are using an older version, update your application or [use Mise](/doc/reference/reference-environment-variables#install-tools-with-mise-package-manager) to download it during deployment. diff --git a/content/doc/applications/frankenphp.md b/content/doc/applications/frankenphp.md index 50e9576a5..f04c1b906 100644 --- a/content/doc/applications/frankenphp.md +++ b/content/doc/applications/frankenphp.md @@ -49,7 +49,7 @@ FrankenPHP runtime only requires a working web application, with an `index.php` FrankenPHP currently deployed version on Clever Cloud is `{{< runtime_version frankenphp >}}` based on PHP `{{< runtime_version frankenphp php >}}` and Caddy server `{{< runtime_version frankenphp caddy >}}`. Virtual machine image includes multiple tools from the PHP ecosystem such as Composer or Symfony CLI. The `php` command available in hooks and scripts uses `frankenphp php-cli` under the hood. -You can use FrankenPHP 1.12.1 with PHP 8.5 and Caddy 2.11.2, by setting the `CC_PHP_VERSION` environment variable to `8.5`. +You can use FrankenPHP 1.12.3 with PHP 8.5 and Caddy 2.11.3, by setting the `CC_PHP_VERSION` environment variable to `8.5`. - [FrankenPHP PHP 8.4 info](https://frankenphpinfo-8.4.cleverapps.io/) - [FrankenPHP PHP 8.5 info](https://frankenphpinfo-8.5.cleverapps.io/) diff --git a/content/doc/applications/scala/play-framework-1.md b/content/doc/applications/scala/play-framework-1.md index c5e12ec61..461989296 100644 --- a/content/doc/applications/scala/play-framework-1.md +++ b/content/doc/applications/scala/play-framework-1.md @@ -31,7 +31,7 @@ Clever Cloud supports Play 1.x applications natively. The present guide explains ### Select Play! 1.x version -Clever Cloud supports Play! **1.2**, **1.3**, **1.4**, **1.5**. You can select the Play! version for your application by setting the `PLAY1_VERSION` [environment variable](#setting-up-environment-variables-on-clever-cloud) (or by putting it in a file named `clevercloud/play1_version`). +Clever Cloud supports Play! **1.2** to **1.11**. You can select the Play! version for your application by setting the `PLAY1_VERSION` [environment variable](#setting-up-environment-variables-on-clever-cloud) (or by putting it in a file named `clevercloud/play1_version`). The `PLAY1_VERSION` environment variable can contain one of the following values: @@ -44,6 +44,7 @@ The `PLAY1_VERSION` environment variable can contain one of the following values * `1.8` or `18` for **Play! 1.8** * `1.9` or `19` for **Play! 1.9** * `1.10` or `110` for **Play! 1.10** +* `1.11` or `111` for **Play! 1.11** ### Play! configuration with application.conf diff --git a/content/doc/applications/static.md b/content/doc/applications/static.md index 6de99f7fc..229b5364c 100644 --- a/content/doc/applications/static.md +++ b/content/doc/applications/static.md @@ -110,7 +110,7 @@ Supported Static Site Generators (SSG) are: - Detected file: `hugo.toml`, `hugo.yaml`, `hugo.json` > [!TIP] Set the Hugo version ->Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.147`, `0.148`, `0.149` (default), `0.150`, `0.151` or `0.152` +>Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.147`, `0.148`, `0.149` (default), `0.150`, `0.151`, `0.152`, `0.159`, `0.160` or `0.161`. From June 15th, only `0.160` and later are supported. ### mdBook diff --git a/data/runtime_versions.yml b/data/runtime_versions.yml index a5521476d..cda42bcde 100644 --- a/data/runtime_versions.yml +++ b/data/runtime_versions.yml @@ -1,7 +1,7 @@ bun: eol_source: https://github.com/oven-sh/bun/releases default: - - 1.3.12 + - 1.3.14 caddy: eol_source: https://github.com/caddyserver/caddy/releases From 5ad1ec81792ed243e6b6c216994a92b90eecc29c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 22 May 2026 13:55:15 +0200 Subject: [PATCH 052/214] addons(postgresql): PostgreSQL 18.4, 17.10, 16.14, 15.18, 14.23 --- .../changelog/2026/05-21-pg-update-18.4.md | 26 +++++++++++++++++++ data/software_versions_shared_dedicated.yml | 12 ++++----- 2 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 content/changelog/2026/05-21-pg-update-18.4.md diff --git a/content/changelog/2026/05-21-pg-update-18.4.md b/content/changelog/2026/05-21-pg-update-18.4.md new file mode 100644 index 000000000..ddaf0ef91 --- /dev/null +++ b/content/changelog/2026/05-21-pg-update-18.4.md @@ -0,0 +1,26 @@ +--- +title: PostgreSQL 18.4, 17.10, 16.14, 15.18, 14.23 are available (security update) +description: Security patches, bug fixes and improvements for PostgreSQL 14 to 18 +date: 2026-05-21 +tags: + - addons + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +New PostgreSQL versions are available for new add-ons and migration: +* PostgreSQL 18.4 +* PostgreSQL 17.10 +* PostgreSQL 16.14 +* PostgreSQL 15.18 +* PostgreSQL 14.23 + +These versions include [over 60 bug fixes and improvements](https://www.postgresql.org/about/news/postgresql-184-1710-1614-1518-and-1423-released-3297), with security patches for 11 CVEs: [CVE-2026-6472](https://nvd.nist.gov/vuln/detail/CVE-2026-6472), [CVE-2026-6473](https://nvd.nist.gov/vuln/detail/CVE-2026-6473), [CVE-2026-6474](https://nvd.nist.gov/vuln/detail/CVE-2026-6474), [CVE-2026-6475](https://nvd.nist.gov/vuln/detail/CVE-2026-6475), [CVE-2026-6476](https://nvd.nist.gov/vuln/detail/CVE-2026-6476), [CVE-2026-6477](https://nvd.nist.gov/vuln/detail/CVE-2026-6477), [CVE-2026-6478](https://nvd.nist.gov/vuln/detail/CVE-2026-6478), [CVE-2026-6479](https://nvd.nist.gov/vuln/detail/CVE-2026-6479), [CVE-2026-6575](https://nvd.nist.gov/vuln/detail/CVE-2026-6575), [CVE-2026-6637](https://nvd.nist.gov/vuln/detail/CVE-2026-6637) and [CVE-2026-6638](https://nvd.nist.gov/vuln/detail/CVE-2026-6638). + +PostgreSQL 14 reaches its end of life on 12 November 2026. Plan a migration to a newer version before that date to keep receiving security updates. Also note that PostgreSQL 11 [is not available anymore](/changelog/2025/03-24-postgresql-11-12-eol/) for migrations on Clever Cloud. + +* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/data/software_versions_shared_dedicated.yml b/data/software_versions_shared_dedicated.yml index 770c206f4..1a5a027f2 100644 --- a/data/software_versions_shared_dedicated.yml +++ b/data/software_versions_shared_dedicated.yml @@ -22,13 +22,13 @@ mysql: pg: dedicated: - - v14.22 - - v15.17 - - v16.13 - - v17.9 - - v18.3 + - v14.23 + - v15.18 + - v16.14 + - v17.10 + - v18.4 dev: - - v15.17 + - v15.18 redis: dedicated: From e1dad8a4bce22c32f8e6666bd4fc74ed19df1061 Mon Sep 17 00:00:00 2001 From: Julien Durillon Date: Wed, 27 May 2026 10:57:56 +0200 Subject: [PATCH 053/214] reference(env vars): add `CC_CACHE_DEPENDENCIES_EXTRA_PATHS` variable in reference --- content/doc/reference/reference-environment-variables.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/doc/reference/reference-environment-variables.md b/content/doc/reference/reference-environment-variables.md index e55b5132b..4e564dbd2 100644 --- a/content/doc/reference/reference-environment-variables.md +++ b/content/doc/reference/reference-environment-variables.md @@ -69,6 +69,7 @@ You can set some tools' version in any runtime (except Docker): | Name | Description | Default value | |-----------------------|------------------------------|--------------------------------| |`CC_CACHE_DEPENDENCIES` | Enable caching of your build dependencies to speed up following builds. | false | +|`CC_CACHE_DEPENDENCIES_EXTRA_PATHS` | Add custom paths to the dependencies cache. Expected format: Colon-separated PATH-style string. Paths are considered relative to APP_HOME. One level of `../` is allowed. E.g. `../.cargo:lib:vendor` will select `/home/bas/.cargo`, `/home/bas/{app_id}/lib` and `/home/bas/{app_id}/vendor` | | |[`CC_DISABLE_BUILD_CACHE_UPLOAD`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Disable creation and upload of cache archive. Restarts won't be speeded up. | `false` | |[`CC_IGNORE_FROM_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Allows to specify paths to ignore when the build cache archive is created. | | |[`IGNORE_FROM_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | (Deprecated) Allows to specify paths to ignore when the build cache archive is created. | | From d5719fea32d0696a4f85aacaa46e90d32e2e18aa Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 27 May 2026 12:41:47 +0200 Subject: [PATCH 054/214] chore: update Hugo minimum supported version --- hugo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo.yaml b/hugo.yaml index 34604903d..7ab573b17 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -7,7 +7,7 @@ module: - path: github.com/imfing/hextra hugoVersion: extended: true - min: 0.151.0 + min: 0.154.5 disableKinds: - taxonomy From affc57849b7728d70d2f33063bd320a9e2bf2afc Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 27 May 2026 12:42:31 +0200 Subject: [PATCH 055/214] chore: use Hugo 0.161 as example version --- .github/copilot-instructions.md | 2 +- .github/workflows/deploy.yml | 2 +- CLAUDE.md | 2 +- README.md | 2 +- content/doc/reference/reference-environment-variables.md | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 839f8222b..74b9c7cd4 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -273,7 +273,7 @@ The site is configured for Clever Cloud hosting with the `static` runtime and th - `CC_WEBROOT="public"` - `CC_STATIC_AUTOBUILD_OUTDIR="public/developers"` - `SERVER_ERROR_PAGE_404="developers/404.html"` -- Optional: `CC_HUGO_VERSION="0.152"` to specify Hugo version (example value) +- Optional: `CC_HUGO_VERSION="0.161"` to specify Hugo version (example value) ## Quality Assurance Requirements diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 007363036..e8bc5bc1d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -35,7 +35,7 @@ jobs: CLEVER_SECRET: ${{ secrets.CLEVER_SECRET }} CLEVER_TOKEN: ${{ secrets.CLEVER_TOKEN }} ORGA_ID: ${{ secrets.ORGA_ID }} - GH_CC_HUGO_VERSION: 0.152 + GH_CC_HUGO_VERSION: 0.161 GH_CC_STATIC_AUTOBUILD_OUTDIR: public/developers GH_CC_WEBROOT: public GH_SERVER_ERROR_PAGE_404: developers/404.html diff --git a/CLAUDE.md b/CLAUDE.md index f017e46fb..6f15fe59a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,7 +90,7 @@ The site is configured for Clever Cloud hosting with the `static` runtime and th - `CC_WEBROOT="public"` - `CC_STATIC_AUTOBUILD_OUTDIR="public/developers"` - `SERVER_ERROR_PAGE_404="developers/404.html"` -- Optional: `CC_HUGO_VERSION="0.152"` to specify Hugo version (example value) +- Optional: `CC_HUGO_VERSION="0.161"` to specify Hugo version (example value) ## Data Management Runtime versions and software compatibility information is maintained in `/data/runtime_versions.yml` and should be kept current with platform capabilities. The site generates various output formats including standard HTML and a special LLMS output format at `/llms.txt` for AI consumption. diff --git a/README.md b/README.md index 2d3a7a6f9..417b5b276 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ SERVER_ERROR_PAGE_404="developers/404.html" ``` > [!TIP] -> You can set the Hugo version with `CC_HUGO_VERSION` with a value like `0.152` +> You can set the Hugo version with `CC_HUGO_VERSION` with a value like `0.161` ## Contributing diff --git a/content/doc/reference/reference-environment-variables.md b/content/doc/reference/reference-environment-variables.md index 4e564dbd2..66c2d0c22 100644 --- a/content/doc/reference/reference-environment-variables.md +++ b/content/doc/reference/reference-environment-variables.md @@ -61,7 +61,7 @@ You can set some tools' version in any runtime (except Docker): | Name | Description | |------|-------------| -| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.152` | | +| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.161` | | | `CC_NODE_VERSION` | Set Node.js version, for example `24`, `23.11` or `22.15.1` | | #### Control build and dependencies cache @@ -386,7 +386,7 @@ When your Python application doesn't use one of the supported backends, with `CC | Name | Description | Default value | |------|-------------|---------------| -| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.152` | | +| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.161` | | | `CC_BUILD_COMMAND` | The command to run during the build phase | | | `CC_OVERRIDE_BUILDCACHE` | Files and path to put in the build cache, separated by a `:` | | | `CC_STATIC_AUTOBUILD_OUTDIR` | The output directory of the static site generator (SSG) auto-build, relative to the root of your application | `/cc_static_autobuilt` | From b8d814170bfe6a1e4f67130f99651a86586a08c4 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 27 May 2026 12:43:07 +0200 Subject: [PATCH 056/214] chore: remove temporary mise.toml file --- mise.toml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 mise.toml diff --git a/mise.toml b/mise.toml deleted file mode 100644 index ab6cd305c..000000000 --- a/mise.toml +++ /dev/null @@ -1,2 +0,0 @@ -[tools] -hugo-extended = "latest" From 5fd16a19989ad14df447bbae3341a710f31a2de7 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 29 May 2026 15:15:45 +0200 Subject: [PATCH 057/214] changelog: Keycloak 26.6.2 --- .../changelog/2026/05-27-keycloak-26.6.2.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 content/changelog/2026/05-27-keycloak-26.6.2.md diff --git a/content/changelog/2026/05-27-keycloak-26.6.2.md b/content/changelog/2026/05-27-keycloak-26.6.2.md new file mode 100644 index 000000000..47e03a8dc --- /dev/null +++ b/content/changelog/2026/05-27-keycloak-26.6.2.md @@ -0,0 +1,32 @@ +--- +title: Keycloak 26.6.2 (security update) +description: Keycloak 26.6.2 ships on Clever Cloud with the 26.6.1 fixes and addresses seventeen CVEs +date: 2026-05-27 +tags: + - addons + - keycloak +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[The release 26.6.2](https://github.com/keycloak/keycloak/releases/tag/26.6.2) of Keycloak is available on Clever Cloud. It brings some enhancements, but mainly security fixes. It also includes those from [26.6.1](https://github.com/keycloak/keycloak/releases/tag/26.6.1). + +Together these releases address seventeen security vulnerabilities: [CVE-2026-4366](https://nvd.nist.gov/vuln/detail/CVE-2026-4366), [CVE-2026-4633](https://nvd.nist.gov/vuln/detail/CVE-2026-4633), [CVE-2026-33870](https://nvd.nist.gov/vuln/detail/CVE-2026-33870), [CVE-2026-33871](https://nvd.nist.gov/vuln/detail/CVE-2026-33871), [CVE-2026-4628](https://nvd.nist.gov/vuln/detail/CVE-2026-4628), [CVE-2026-4630](https://nvd.nist.gov/vuln/detail/CVE-2026-4630), [CVE-2026-5588](https://nvd.nist.gov/vuln/detail/CVE-2026-5588), [CVE-2026-6856](https://nvd.nist.gov/vuln/detail/CVE-2026-6856), [CVE-2026-7307](https://nvd.nist.gov/vuln/detail/CVE-2026-7307), [CVE-2026-7504](https://nvd.nist.gov/vuln/detail/CVE-2026-7504), [CVE-2026-7507](https://nvd.nist.gov/vuln/detail/CVE-2026-7507), [CVE-2026-7571](https://nvd.nist.gov/vuln/detail/CVE-2026-7571), [CVE-2026-37978](https://nvd.nist.gov/vuln/detail/CVE-2026-37978), [CVE-2026-37979](https://nvd.nist.gov/vuln/detail/CVE-2026-37979), [CVE-2026-37980](https://nvd.nist.gov/vuln/detail/CVE-2026-37980), [CVE-2026-37981](https://nvd.nist.gov/vuln/detail/CVE-2026-37981) and [CVE-2026-37982](https://nvd.nist.gov/vuln/detail/CVE-2026-37982). + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_KEYCLOAK_VERSION` of the underlying Java application to `26.6.2` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever keycloak version check yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId 26.6.2 +``` + +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak) From 62a9e964423d4b68bd1bf849170a04f18096248e Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 29 May 2026 15:00:28 +0200 Subject: [PATCH 058/214] changelog: Otoroshi 17.16 --- .../changelog/2026/05-29-otoroshi-17.16.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 content/changelog/2026/05-29-otoroshi-17.16.md diff --git a/content/changelog/2026/05-29-otoroshi-17.16.md b/content/changelog/2026/05-29-otoroshi-17.16.md new file mode 100644 index 000000000..81586c3e9 --- /dev/null +++ b/content/changelog/2026/05-29-otoroshi-17.16.md @@ -0,0 +1,65 @@ +--- +title: Otoroshi 17.16 brings user analytics, distributed rate limiting and new HTTP standards plugins +description: User analytics dashboards and alerts, distributed rate limiting, RFC 9421/9440/9728 plugins, reworked Kubernetes deployments and an LLM AI Assistant +date: 2026-05-29 +tags: + - addons + - otoroshi +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Otoroshi v17.16.0](https://github.com/MAIF/otoroshi/releases/tag/v17.16.0) is available. This release focuses on observability, API lifecycle management, Kubernetes deployments and a set of brand-new HTTP standards plugins. It also ships an updated LLM extension featuring an AI Assistant for the admin interface and an updated WAF extension. The Otoroshi documentation has been substantially expanded as well. + +### User analytics, dashboards and a global event stream + +Otoroshi gains a complete user analytics stack. A PostgreSQL-based analytics exporter stores and queries events at scale, and you can now build your own dashboards with a graphical widget editor, drill-down support and a set of default dashboards shipped out of the box. User-defined alerts are built from analytics queries through a graphical condition editor, with a dedicated alerting interface and a scheduled evaluation job. Dashboards, alerts and analytics queries are all integrated with RBAC. + +A new global node event stream page in the back office streams audit, alert and analytics events from current node, with pagination, capped buffers and per-type filtering. Data exporters also accept pluggable custom filters and a project phase, and no longer generate one event per filtered event. + +- [Learn more about dashboards, analytics queries and alerts](https://maif.github.io/otoroshi/manual/docs/topics/user-analytics/) + +### API management and new HTTP standards plugins + +API management hardens the split between draft and production: a clear version banner, read-only production views and plan lifecycle enforcement that locks the `Api` entity fields once a plan is published. A getting-started stepper guides newcomers, and the draft subscription listing, top bar search and API key plan editor have all been cleaned up. + +This release introduces three plugins implementing recent HTTP standards. The HTTP Message Signatures plugins ([RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421)) sign and verify requests and responses, the `NgRfc9440ClientCertHeader` plugin forwards client certificates through the standard `Client-Cert` and `Client-Cert-Chain` headers ([RFC 9440](https://datatracker.ietf.org/doc/html/rfc9440)), and an OAuth 2.0 Protected Resource Metadata plugin implements [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728). New JSON validator plugins based on JSON Schemas are also available, and the `OAuth2Caller` plugin can now target a specific resource for the generated token or rely on an OAuth2 authentication module. + +- [HTTP Message Signatures (RFC 9421) tutorial](https://maif.github.io/otoroshi/manual/docs/tutorials/http-message-signatures-rfc9421/) +- [Learn more about RFC 9440 Client-Cert headers](https://maif.github.io/otoroshi/manual/docs/topics/tls/#rfc-9440--client-cert--client-cert-chain-headers) + +### Distributed rate limiting, mailers and exporters + +A new distributed rate-limiting strategy backed by atomic Lua scripts on Redis provides predictable, cluster-aware quotas across nodes. It is a drop-in alternative to the previous distributed strategy and is configurable per route. Scaleway TEM and MailPace join the supported mailers. + +### Reworked Kubernetes deployments + +The Helm chart has been almost entirely rewritten with first-class cluster mode (leader and worker deployments), a Redis secured by default based on the CloudPirates Redis chart, and proper HPA, PDB, NetworkPolicies, RBAC, webhooks and certificate templates. The Kustomize manifests reach feature parity with Helm through a component-based split, and the generated CRDs now expose previously missing entities, including APIs, dashboards, alerts and the new analytics resources. + +### LLM extension: an AI Assistant and Meta MCP + +This release includes LLM extension [0.0.76](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.76), which adds an AI Assistant to the Otoroshi admin interface. The assistant can search the documentation, query the admin API and run administrative tasks through its Search, Doc and Execute tools, with a conversational chat window, streaming responses, light and dark themes and API key-based access. A hybrid semantic and lexical search engine backs documentation queries. + +The extension also introduces a Meta MCP connector that aggregates and proxies multiple MCP servers with dynamic capabilities, an Otoroshi MCP server plugin exposing the assistant tools, and image generation support in the `Response` and `OpenResponse` plugins. The Monaco editor is now used across all administration pages of the extension. + +### Updated bundled plugins + +Every plugin delivered with Otoroshi on Clever Cloud has been rebuilt against 17.16.0. The WAF extension ([0.0.9](https://github.com/cloud-apim/otoroshi-waf-extension/releases/tag/0.0.9)) upgrades the OWASP Core Rule Set to v4.26.0. + +You can update through your add-on dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_OTOROSHI_VERSION` of the underlying Java application to `v17.16.0_1779982854` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever otoroshi version check yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId v17.16.0_1779982854 +``` + +- [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) From 5e2a8ce10d2124be46175c6e31395d44aef87451 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 10 Jun 2026 09:06:00 +0200 Subject: [PATCH 059/214] =?UTF-8?q?changelog:=20S=C5=8Dzu=202.1.0=20(#965)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 What does this PR do? This PR adds an entry about Sōzu 2.1.0 --- ## 🧪 Type of Change - [ ] ⚠️ Bug fix - [x] 📅 Changelog update - [ ] 📚 Documentation update - [ ] ✨ New content/feature - [ ] 🔧 Technical/maintenance --- ## ✅ Quick Checklist - [x] I have read the [contributing guidelines](https://github.com/CleverCloud/documentation/blob/main/CONTRIBUTING.md) - [x] The content is accurate and links work - [x] The site builds without errors --- ## 👥 Reviewers @FlorentinDUBOIS --- content/changelog/2026/06-05-sozu-2.1.0.md | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 content/changelog/2026/06-05-sozu-2.1.0.md diff --git a/content/changelog/2026/06-05-sozu-2.1.0.md b/content/changelog/2026/06-05-sozu-2.1.0.md new file mode 100644 index 000000000..3977c2227 --- /dev/null +++ b/content/changelog/2026/06-05-sozu-2.1.0.md @@ -0,0 +1,24 @@ +--- +title: Sōzu 2.1.0 is available with HTTP/2 support +date: 2026-06-05 +tags: + - sozu +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 + - name: Florentin Dubois + link: https://github.com/FlorentinDUBOIS + image: https://github.com/FlorentinDUBOIS.png?size=40 +description: UDP load balancing joins the HTTP/2 stack, security hardening and operator tooling introduced with Sōzu 2.0 +excludeSearch: true +--- + +[Sōzu](https://www.sozu.io) [2.1.0](https://github.com/sozu-proxy/sozu/releases/tag/2.1.0) is available. It consolidates every improvement shipped since the [2.0.0 milestone](https://github.com/sozu-proxy/sozu/releases/tag/2.0.0): a rewritten HTTP/2 frontend stack supporting the full HTTP/1 and HTTP/2 protocol matrix, a `sozu top` TUI for live monitoring, pluggable cryptographic providers, per-IP rate limiting, frontend routing features such as HSTS, redirects and URL rewrites, control-plane audit logging and a comprehensive metrics rewrite. It also hardens the proxy against HTTP/2 attacks such as Rapid Reset, CONTINUATION flood and MadeYouReset. + +Sōzu 2.1.0 introduces first-class UDP load balancing as an opt-in listener type, with HRW and Maglev source-hash algorithms, flow affinity, PROXY protocol v2 to backends, active health checks and dedicated metrics. Patch releases since 2.0.0 added a wall-clock `start_time` field in access logs for OpenTelemetry span reconstruction and further HTTP/2 flow-control protections. This release also fixes a hot-reconfiguration bug that silently deactivated listeners, and adds about 1,100 debug assertions across the codebase for stronger correctness validation. + +This new version currently serves `cleverapps.io` domains as a testing phase, before a wider rollout to custom domains. + +- [Read about Sōzu 2.0, turning a reverse proxy into a programmable edge](https://www.clever.cloud/blog/engineering/2026/05/29/sozu-2-0-reverse-proxy-programmable-edge/) +- [Learn more about Sōzu](https://github.com/sozu-proxy/sozu/releases) {{< icon "github" >}} From db7a5ca0f25f66aa968fbd63dc451af2b87d54c3 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 10 Jun 2026 08:28:17 +0200 Subject: [PATCH 060/214] changelog: Redis 8.8 --- content/changelog/2026/06-04-redis-8.8.md | 24 +++++++++++++++++++++ data/software_versions_shared_dedicated.yml | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 content/changelog/2026/06-04-redis-8.8.md diff --git a/content/changelog/2026/06-04-redis-8.8.md b/content/changelog/2026/06-04-redis-8.8.md new file mode 100644 index 000000000..ea1dd7527 --- /dev/null +++ b/content/changelog/2026/06-04-redis-8.8.md @@ -0,0 +1,24 @@ +--- +title: Redis 8.8 is available with new commands and field-level notifications +description: A new INCREX rate limiting command, XNACK for Streams, hash field notifications and query engine improvements come with Redis 8.8 +date: 2026-06-04 +tags: + - addons + - redis +authors: + - name: Aurélien Hébert + link: https://github.com/aurrelhebert + image: https://github.com/aurrelhebert.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated Redis™ to [release 8.8.0](https://github.com/redis/redis/releases/tag/8.8.0). It introduces the `INCREX` command, a window counter rate limiter combining increment, bounds and expiration in a single operation, and the `XNACK` command letting Streams consumers explicitly release pending messages. Keyspace notifications now work at field level for hashes, so you can subscribe to changes on individual hash fields. + +This release also adds a `COUNT` aggregator to `ZUNION`, `ZINTER` and their store variants, support for multiple aggregators in a single TimeSeries range command, and query engine improvements with profiling support for hybrid searches. It ships bug fixes for memory tracking, cluster topology handling and module memory leaks, along with performance optimizations. + +Redis™ 8.8 is available for new add-ons. Those already deployed can upgrade through migration. + +- [Learn more about Redis™ on Clever Cloud](/doc/addons/redis/) diff --git a/data/software_versions_shared_dedicated.yml b/data/software_versions_shared_dedicated.yml index 1a5a027f2..de6bc9310 100644 --- a/data/software_versions_shared_dedicated.yml +++ b/data/software_versions_shared_dedicated.yml @@ -32,4 +32,4 @@ pg: redis: dedicated: - - v8.4.0 + - v8.8.0 From f912eea219370f8d637b56c5f13bb35e19ed5a63 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 10 Jun 2026 08:25:46 +0200 Subject: [PATCH 061/214] changelog: Otoroshi 17.16.1 --- .../changelog/2026/06-04-otoroshi-17.16.1.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 content/changelog/2026/06-04-otoroshi-17.16.1.md diff --git a/content/changelog/2026/06-04-otoroshi-17.16.1.md b/content/changelog/2026/06-04-otoroshi-17.16.1.md new file mode 100644 index 000000000..1dfd44e57 --- /dev/null +++ b/content/changelog/2026/06-04-otoroshi-17.16.1.md @@ -0,0 +1,32 @@ +--- +title: Otoroshi 17.16.1 is available with AlphaEdge and OCR support +description: A patch release fixing workflows and OAuth2Caller, shipped with LLM extension 0.0.78 introducing OCR models and the AlphaEdge provider +date: 2026-06-04 +tags: + - addons + - otoroshi +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Otoroshi v17.16.1](https://github.com/MAIF/otoroshi/releases/tag/v17.16.1) is available. This patch release fixes the workflow backend response parsing, an `OAuth2Caller` bug where the plugin always tried to read `auth_ref`, and a Monaco editor issue swallowing scroll events. It also cleans up the HTTP client workflow node interface. + +This release ships with LLM extension [0.0.78](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.78), which introduces OCR models as a first-class entity alongside audio, image, embedding and moderation models. You can expose them through a dedicated plugin, the unified OpenAI-compatible API or the `ocr_call` workflow function, with Mistral models support. The French provider [AlphaEdge](https://www.alphaedge-ai.com/) joins the extension for both speech transcription, with diarization and linguistic post-correction options, and optical character recognition. + +You can update through your add-on dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_OTOROSHI_VERSION` of the underlying Java application to `v17.16.1_1780575469` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever otoroshi version check yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId v17.16.1_1780575469 +``` + +- [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) From 110d51223a2f3a8226b51def9c9be9135d76c002 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 10 Jun 2026 17:37:15 +0200 Subject: [PATCH 062/214] addons(materia-kv): fix GraphQL expireAt --- content/doc/addons/materia-kv.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/content/doc/addons/materia-kv.md b/content/doc/addons/materia-kv.md index f9272c962..251efdc56 100644 --- a/content/doc/addons/materia-kv.md +++ b/content/doc/addons/materia-kv.md @@ -263,7 +263,7 @@ OK In addition to the Redis API, Materia KV exposes a **GraphQL** endpoint. It reads from the same keyspace as the Redis API layer — every key you write through the Redis API is immediately queryable through GraphQL, with no synchronization layer in between. Both interfaces authenticate with the same token, and that token scopes each request to your add-on's data on the shared cluster. -The GraphQL layer is **read-only** today — mutations aren't supported yet. Use the Redis API for writes. +The GraphQL layer is **read-only** today — the schema exposes no mutation type, and any mutation request is rejected with a `Schema is not configured for mutations.` error. Use the Redis API for writes. ### Endpoint and authentication @@ -334,6 +334,12 @@ The schema exposes a single root type, `MateriaKvQuery`, with queries for string Single-key queries (`string`, `hash`, `hashField`, `getSetMembers`) return a **nullable** object — `null` when the key doesn't exist. Pattern and batch queries return **non-null lists** (possibly empty). +### Key expiration + +String, hash and set results all expose the key's expiration through an `expireAt` field, typed with the schema's `DateTime` scalar: an absolute UTC instant serialized as an RFC 3339 (ISO 8601) string, such as `2026-06-10T15:40:54.134+00:00`. Keys without a time to live return `null`. + +The Redis API remains the place where you set, update or clear a TTL (`EXPIRE`, `PEXPIRE`, `PERSIST`, `SET ... EX`): GraphQL reflects the resulting absolute instant on the next read. To compute a countdown in your client, parse `expireAt` as a timestamp and subtract the current time. + ### Query examples Fetch a single string, including its expiration: @@ -392,7 +398,7 @@ JSON documents written via `JSON.SET` are stored on top of strings. The schema h - `strings(keys: [...])` has the same hard limit: **at most 100 keys** per call. Chunk larger batches on the client side. - Pattern-based queries (`hashesByPattern`, `setsByPattern`, `hashFieldsByPattern`) are not paginated: each call returns its full result set in one response. Keep patterns focused to avoid oversized payloads. - Glob patterns follow the Redis `KEYS`/`SCAN` syntax (`*`, `?`, `[abc]`). -- The `expireAt` field is an absolute instant (ISO 8601), not a remaining TTL — parse it as an ISO 8601 timestamp in your client, then subtract the current time to compute a countdown. +- The `expireAt` field is an absolute instant (a `DateTime` scalar in RFC 3339 / ISO 8601 format), not a remaining TTL — see [Key expiration](#key-expiration) above. - Authentication errors return HTTP 401 with the message in the `errors` array. Every other GraphQL error (wrong-type reads, mutation requests, validation errors) returns HTTP 200 with `errors` populated. ## Demos and examples From 172973a06a79717cb27bff86440b494311ce4d36 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 10 Jun 2026 08:30:27 +0200 Subject: [PATCH 063/214] changelog: Matomo 5.11 --- content/changelog/2026/06-09-matomo-5.11.md | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 content/changelog/2026/06-09-matomo-5.11.md diff --git a/content/changelog/2026/06-09-matomo-5.11.md b/content/changelog/2026/06-09-matomo-5.11.md new file mode 100644 index 000000000..ca75e2ad4 --- /dev/null +++ b/content/changelog/2026/06-09-matomo-5.11.md @@ -0,0 +1,25 @@ +--- +title: Matomo 5.11 is available +description: Add descriptions to your sites and custom dimensions, send scheduled reports over custom date ranges +date: 2026-06-09 +tags: + - addons + - matomo +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Our [Matomo](https://matomo.org/) add-on has been updated to version `5.11.0` which is now used by default. This release lets you document your analytics setup directly in Matomo: sites accept a description of up to 255 characters, and custom dimensions a description of up to 1000 characters, both available through the interface and the API. + +Scheduled reports can now be sent over a custom date range, in addition to the usual periods. Reports gain finer control over flattening and exports, CSV and TSV exports now replace carriage return characters to keep files consistent, and themes benefit from a new alternative border color variable. As usual, this version ships its batch of bug fixes. + +You can deploy this release from our [Console](https://console.clever-cloud.com) or [Clever Tools](/doc/cli/). Existing customers' add-ons are already up-to-date. + +- [Learn more about Matomo 5.11](https://matomo.org/changelog/matomo-5-11-0/) +- [Learn more about Matomo on Clever Cloud](/doc/addons/matomo/) From 241833e0f7840b01387c800d475a8c95ea77d935 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 11 Jun 2026 10:22:41 +0200 Subject: [PATCH 064/214] changelog: Metabase 61 --- content/changelog/2026/05-30-metabase-61.md | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 content/changelog/2026/05-30-metabase-61.md diff --git a/content/changelog/2026/05-30-metabase-61.md b/content/changelog/2026/05-30-metabase-61.md new file mode 100644 index 000000000..f592a804e --- /dev/null +++ b/content/changelog/2026/05-30-metabase-61.md @@ -0,0 +1,35 @@ +--- +title: "Metabase 61 is available, with AI governance, dashboards-as-code and Security Center" +description: AI access controls, token limits, Metabot customization, AI usage analytics, dashboards-as-code, metrics math, Security Center and more +date: 2026-05-30 +tags: + - addons + - metabase +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +The `x.61` branch of Metabase is now available on Clever Cloud. It focuses on AI governance: control which user groups can access AI features such as Metabot, SQL generation and auto-generated transforms, set token limits per instance or per user group with daily, weekly or monthly resets, customize Metabot with your own name, icon and system prompts, and track token spend and feature usage through a pre-made AI usage analytics dashboard. + +It also introduces dashboards-as-code to create dashboards from an AI terminal such as Claude Code or Cursor with git-backed validation, arithmetic expressions across metrics in the metrics explorer, custom expressions written by Metabot in the query builder, and a Security Center with targeted alerts for your instance configuration. Embedded analytics gains usage analytics, guest token auto-renewal, a themes editor, mobile-optimized SDK components and a `useMetabot` React hook. Some of these features require the enterprise edition (EE). + +This branch is not the default for now if you use `community-latest`. You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_METABASE_VERSION` of the underlying Java application to `0.61` or `1.61` for the enterprise edition (EE) and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever metabase version check yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId 0.61 +``` + +- [Learn more about Metabase 61](https://www.metabase.com/releases/metabase-61) +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) + +{{< youtube id="jU8ua9OHvTY" >}} From 81b4d2a7c46dde4bab7edfe067d74e08e0fac829 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 11 Jun 2026 10:22:46 +0200 Subject: [PATCH 065/214] changelog: Metabase 62 --- content/changelog/2026/06-11-metabase-62.md | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 content/changelog/2026/06-11-metabase-62.md diff --git a/content/changelog/2026/06-11-metabase-62.md b/content/changelog/2026/06-11-metabase-62.md new file mode 100644 index 000000000..31e13a5f2 --- /dev/null +++ b/content/changelog/2026/06-11-metabase-62.md @@ -0,0 +1,33 @@ +--- +title: "Metabase 62 is available, with custom visualizations, schema viewer and a CLI" +description: Custom visualizations, schema viewer, Metabase CLI, centralized alert management, subcollections, new MCP server tools and more +date: 2026-06-11 +tags: + - addons + - metabase +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +The `x.62` branch of Metabase is now available on Clever Cloud. It brings custom visualizations with built-in drills and tooltips, a schema viewer to display entity-relationship diagrams of your databases, a Metabase CLI built on the API to create dashboards, transforms and metrics from the terminal or an AI agent, centralized alert management, and subcollections in the Library to organize tables and metrics with inherited permissions. Some of these features require the enterprise edition (EE). + +On the AI side, this release adds official connectors for the OpenAI Codex and Claude marketplaces, new MCP server tools to read entities, create collections and execute SQL, and interactive Metabase charts rendered directly in AI clients. The official Claude connector only works with Metabase Cloud instances: for a Metabase running on Clever Cloud, add a custom Claude connector pointing to your instance's MCP server URL. Embedding security improves with JWT tokens passed via POST instead of GET, programmatic filter control through a new `parameters` prop, and a streamlined embedding wizard. This release also includes multiple enhancements and bug fixes. + +This branch is not the default for now if you use `community-latest`. You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_METABASE_VERSION` of the underlying Java application to `0.62` or `1.62` for the enterprise edition (EE) and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever metabase version check yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId 0.62 +``` + +- [Learn more about Metabase 62](https://www.metabase.com/changelog/62) +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) From 733d6b8f0ef286381add9e8f62dc6c4c35286d4d Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 11 Jun 2026 10:23:26 +0200 Subject: [PATCH 066/214] changelog: Keycloak 26.6.3 --- .../changelog/2026/06-11-keycloak-26.6.3.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 content/changelog/2026/06-11-keycloak-26.6.3.md diff --git a/content/changelog/2026/06-11-keycloak-26.6.3.md b/content/changelog/2026/06-11-keycloak-26.6.3.md new file mode 100644 index 000000000..eec510bba --- /dev/null +++ b/content/changelog/2026/06-11-keycloak-26.6.3.md @@ -0,0 +1,32 @@ +--- +title: Keycloak 26.6.3 (security update) +description: Keycloak 26.6.3 ships on Clever Cloud with enhancements and addresses sixteen CVEs +date: 2026-06-11 +tags: + - addons + - keycloak +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[The release 26.6.3](https://github.com/keycloak/keycloak/releases/tag/26.6.3) of Keycloak is available on Clever Cloud. It brings some enhancements and bug fixes, but mainly security fixes. + +This release addresses sixteen security vulnerabilities: [CVE-2026-0707](https://nvd.nist.gov/vuln/detail/CVE-2026-0707), [CVE-2026-4800](https://nvd.nist.gov/vuln/detail/CVE-2026-4800), [CVE-2026-4874](https://nvd.nist.gov/vuln/detail/CVE-2026-4874), [CVE-2026-7500](https://nvd.nist.gov/vuln/detail/CVE-2026-7500), [CVE-2026-8830](https://nvd.nist.gov/vuln/detail/CVE-2026-8830), [CVE-2026-8922](https://nvd.nist.gov/vuln/detail/CVE-2026-8922), [CVE-2026-9087](https://nvd.nist.gov/vuln/detail/CVE-2026-9087), [CVE-2026-9088](https://nvd.nist.gov/vuln/detail/CVE-2026-9088), [CVE-2026-9704](https://nvd.nist.gov/vuln/detail/CVE-2026-9704), [CVE-2026-9791](https://nvd.nist.gov/vuln/detail/CVE-2026-9791), [CVE-2026-9792](https://nvd.nist.gov/vuln/detail/CVE-2026-9792), [CVE-2026-9794](https://nvd.nist.gov/vuln/detail/CVE-2026-9794), [CVE-2026-9801](https://nvd.nist.gov/vuln/detail/CVE-2026-9801), [CVE-2026-9802](https://nvd.nist.gov/vuln/detail/CVE-2026-9802), [CVE-2026-37977](https://nvd.nist.gov/vuln/detail/CVE-2026-37977) and [CVE-2026-42581](https://nvd.nist.gov/vuln/detail/CVE-2026-42581). + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_KEYCLOAK_VERSION` of the underlying Java application to `26.6.3` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever keycloak version check yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId 26.6.3 +``` + +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak) From f4145371a8e88f84979be05825de9d5da02b1693 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 16 Jun 2026 16:57:07 +0200 Subject: [PATCH 067/214] changelog: Otoroshi 17.16.1 with LLM Extension 0.0.79 --- .../2026/06-16-otoroshi-17.16.1-llm-0.0.79.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 content/changelog/2026/06-16-otoroshi-17.16.1-llm-0.0.79.md diff --git a/content/changelog/2026/06-16-otoroshi-17.16.1-llm-0.0.79.md b/content/changelog/2026/06-16-otoroshi-17.16.1-llm-0.0.79.md new file mode 100644 index 000000000..542037e30 --- /dev/null +++ b/content/changelog/2026/06-16-otoroshi-17.16.1-llm-0.0.79.md @@ -0,0 +1,36 @@ +--- +title: Otoroshi 17.16.1 is available with web search (including Staan) and AI router support +description: A new build of Otoroshi 17.16.1 shipped with LLM extension 0.0.79, introducing search engines, AI routers, circuit breaking and OpenRouter multi-modal support +date: 2026-06-16 +tags: + - addons + - otoroshi +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Otoroshi v17.16.1](https://github.com/MAIF/otoroshi/releases/tag/v17.16.1) is available on Clever Cloud with the same core as the previous [release](/changelog/2026/06-04-otoroshi-17.16.1/), but with a bumped LLM extension [0.0.79](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.79). + +This version introduces search engines as a first-class entity, with seven providers including [Staan](https://staan.ai/), the sovereign search engine [announced today](https://x.com/Qwant_FR/status/2066539860022718695) by Qwant, alongside Tavily, Brave Search, SearXNG, Google Custom Search and DuckDuckGo. You can use them as LLM tools, expose them through an HTTP API or call them from a workflow function. + +The new AI router adds three routing strategies: a code-router that selects the cheapest model meeting a quality threshold, an auto-router that relies on a judge LLM for prompt-aware routing, and a fusion-router that synthesises responses from several candidates. + +The extension also adds a per-provider circuit breaker, with configurable failure thresholds and cooldown windows for better fallback handling, and a plugin exposing call metadata such as model, provider, token usage, latency and cost through `x-otoroshi-llm-*` response headers. A mock-response decorator lets you short-circuit calls to test provider fallbacks without real API requests, OpenRouter now supports audio, image and video beyond chat, and the dashboard gains budget reset buttons. + +You can update through your add-on dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_OTOROSHI_VERSION` of the underlying Java application to `v17.16.1_1781607455` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever otoroshi version check yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId v17.16.1_1781607455 +``` + +- [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) From 7fcbade262aaff808b6659dda6165f439e8767a7 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 25 Jun 2026 09:07:20 +0200 Subject: [PATCH 068/214] changelog: images updates, 2026W25 --- content/changelog/2026/06-17-images-update.md | 53 +++++++++++++++++++ content/doc/applications/frankenphp.md | 2 +- content/doc/applications/static.md | 2 +- data/runtime_versions.yml | 6 +-- 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 content/changelog/2026/06-17-images-update.md diff --git a/content/changelog/2026/06-17-images-update.md b/content/changelog/2026/06-17-images-update.md new file mode 100644 index 000000000..5a9c21fc1 --- /dev/null +++ b/content/changelog/2026/06-17-images-update.md @@ -0,0 +1,53 @@ +--- +title: "Images update: Static Web Server 2.43, Yarn 4.16, Rust 1.96, Hugo 0.162" +description: All runtimes updated, PHP included, with fresh Rust, Yarn and Static Web Server releases. Hugo now requires 0.160 or newer. +date: 2026-06-17 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images. Deployment is in progress for all our users. + +* **Common:** + * Apache 2.4.68 + * Chromium 149.0.7827.114 + * Mise 2026.6.0 + * NGINX 1.30.2 + * OAuth2 Proxy 7.15.3 + * Redis 8.8.0 +* **Docker:** + * Update to 29.5.2 +* **Erlang/Elixir:** + * Rebar 3.27.0 +* **FrankenPHP:** + * Update to 1.12.4 (For `CC_PHP_VERSION=8.5`) +* **Go:** + * Go 1.26.4 +* **Node.js & Bun:** + * Yarn 4.16.0 +* **PHP:** + * Update to 8.4.22 + * Update to 8.5.7 + * Composer 1.20.28 + * Composer 2.2.28 + * Composer 2.10.1 +* **Python:** + * Update to 3.13.14 + * Update to 3.14.6 + * pip 26.1.2 + * uv 0.11.21 +* **Rust:** + * Rust 1.96.0 +* **Static:** + * Hugo 0.162.1 + * Static Web Server 2.43.0 + +## Hugo version update + +Starting with next release, we will only support Hugo 0.160 and newer. If you are using an older version, update your application or [use Mise](/doc/reference/reference-environment-variables#install-tools-with-mise-package-manager) to download it during deployment. diff --git a/content/doc/applications/frankenphp.md b/content/doc/applications/frankenphp.md index f04c1b906..f340037b3 100644 --- a/content/doc/applications/frankenphp.md +++ b/content/doc/applications/frankenphp.md @@ -49,7 +49,7 @@ FrankenPHP runtime only requires a working web application, with an `index.php` FrankenPHP currently deployed version on Clever Cloud is `{{< runtime_version frankenphp >}}` based on PHP `{{< runtime_version frankenphp php >}}` and Caddy server `{{< runtime_version frankenphp caddy >}}`. Virtual machine image includes multiple tools from the PHP ecosystem such as Composer or Symfony CLI. The `php` command available in hooks and scripts uses `frankenphp php-cli` under the hood. -You can use FrankenPHP 1.12.3 with PHP 8.5 and Caddy 2.11.3, by setting the `CC_PHP_VERSION` environment variable to `8.5`. +You can use FrankenPHP 1.12.4 with PHP 8.5.7 and Caddy 2.11.4, by setting the `CC_PHP_VERSION` environment variable to `8.5`. - [FrankenPHP PHP 8.4 info](https://frankenphpinfo-8.4.cleverapps.io/) - [FrankenPHP PHP 8.5 info](https://frankenphpinfo-8.5.cleverapps.io/) diff --git a/content/doc/applications/static.md b/content/doc/applications/static.md index 229b5364c..972e7e3c8 100644 --- a/content/doc/applications/static.md +++ b/content/doc/applications/static.md @@ -110,7 +110,7 @@ Supported Static Site Generators (SSG) are: - Detected file: `hugo.toml`, `hugo.yaml`, `hugo.json` > [!TIP] Set the Hugo version ->Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.147`, `0.148`, `0.149` (default), `0.150`, `0.151`, `0.152`, `0.159`, `0.160` or `0.161`. From June 15th, only `0.160` and later are supported. +>Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.147`, `0.148`, `0.149` (default), `0.150`, `0.151`, `0.152`, `0.159`, `0.160`, `0.161` or `0.162`. ### mdBook diff --git a/data/runtime_versions.yml b/data/runtime_versions.yml index cda42bcde..80b5c7b19 100644 --- a/data/runtime_versions.yml +++ b/data/runtime_versions.yml @@ -56,7 +56,7 @@ java: node: eol_source: https://nodejs.org/en/about/releases/ default: - - 24.15.0 (npm 11.12.1) + - 24.16.0 (npm 11.13.0) php: eol_source: https://www.php.net/supported-versions.php @@ -80,7 +80,7 @@ php: sws: eol_source: https://github.com/static-web-server/static-web-server/releases default: - - "2.42.0" + - "2.43.0" varnish: eol_source: https://varnish-cache.org/releases/ @@ -90,7 +90,7 @@ varnish: varnish-modules: eol_source: https://github.com/varnish/varnish-modules/releases default: - - "0.27" + - "0.28" v: eol_source: https://github.com/vlang/v/releases From 38490e8a575619e6cf4ad87bb6a3575bf1fd72f5 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 1 Jul 2026 12:52:24 +0200 Subject: [PATCH 069/214] ci: move to Hugo 0.163 --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e8bc5bc1d..b3dfb7bd1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -35,7 +35,7 @@ jobs: CLEVER_SECRET: ${{ secrets.CLEVER_SECRET }} CLEVER_TOKEN: ${{ secrets.CLEVER_TOKEN }} ORGA_ID: ${{ secrets.ORGA_ID }} - GH_CC_HUGO_VERSION: 0.161 + GH_CC_HUGO_VERSION: 0.163 GH_CC_STATIC_AUTOBUILD_OUTDIR: public/developers GH_CC_WEBROOT: public GH_SERVER_ERROR_PAGE_404: developers/404.html From 68bca1c76998bd5515777065d4e9d2afbc1c1390 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 1 Jul 2026 13:04:18 +0200 Subject: [PATCH 070/214] changelog: images updates, 2026W27 --- content/changelog/2026/07-01-images-update.md | 34 +++++++++++++++++++ content/doc/applications/static.md | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 content/changelog/2026/07-01-images-update.md diff --git a/content/changelog/2026/07-01-images-update.md b/content/changelog/2026/07-01-images-update.md new file mode 100644 index 000000000..73a5464e9 --- /dev/null +++ b/content/changelog/2026/07-01-images-update.md @@ -0,0 +1,34 @@ +--- +title: "Images update: Gradle 9.6, Hugo 0.163, Docker 29.6" +description: A tools bump release, Hugo 0.163 is now the default for static applications +date: 2026-07-01 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images. Deployment is in progress for all our users. + +* **Common:** + * cURL 8.21.0 + * Mise 2026.6.14 + * NGINX 1.30.3 +* **Docker:** + * Update to 29.6.1 +* **Java:** + * Gradle 9.6.1 +* **PHP:** + * `mod_ssl` module is now loaded +* **Python:** + * uv 0.11.25 +* **Static:** + * Hugo 0.163.3 + +## Hugo version update + +Starting with this release, we only support Hugo 0.160 and newer. If you are using an older version, update your application or [use Mise](/doc/reference/reference-environment-variables#install-tools-with-mise-package-manager) to download it during deployment. If you don't set `CC_HUGO_VERSION`, `0.163` is now the default. diff --git a/content/doc/applications/static.md b/content/doc/applications/static.md index 972e7e3c8..d088e0b03 100644 --- a/content/doc/applications/static.md +++ b/content/doc/applications/static.md @@ -110,7 +110,7 @@ Supported Static Site Generators (SSG) are: - Detected file: `hugo.toml`, `hugo.yaml`, `hugo.json` > [!TIP] Set the Hugo version ->Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.147`, `0.148`, `0.149` (default), `0.150`, `0.151`, `0.152`, `0.159`, `0.160`, `0.161` or `0.162`. +>Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.160`, `0.161` , `0.162` or `0.163` (default). ### mdBook From defb2ba744fb4bc50deb572fbd57bbc201868d4c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 1 Jul 2026 14:38:11 +0200 Subject: [PATCH 071/214] changelog: Otoroshi 17.17 with LLM extension 0.0.82 --- .../changelog/2026/06-30-otoroshi-17.17.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 content/changelog/2026/06-30-otoroshi-17.17.md diff --git a/content/changelog/2026/06-30-otoroshi-17.17.md b/content/changelog/2026/06-30-otoroshi-17.17.md new file mode 100644 index 000000000..f130e33bb --- /dev/null +++ b/content/changelog/2026/06-30-otoroshi-17.17.md @@ -0,0 +1,49 @@ +--- +title: Otoroshi 17.17 brings RFC 7662 token introspection and an LLM extension with A2A and MCP virtual servers +description: Otoroshi 17.17.0 adds RFC 7662 token introspection, hardened API key validation and Elasticsearch 8 compatibility, and ships LLM extension 0.0.82 with A2A, MCP virtual servers, gateway discovery and local PII redaction +date: 2026-06-30 +tags: + - addons + - otoroshi +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Otoroshi v17.17.0](https://github.com/MAIF/otoroshi/releases/tag/v17.17.0) is available on Clever Cloud. This release adds standards-based token introspection, hardens API key validation and improves Elasticsearch compatibility. It also ships LLM extension [0.0.82](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.82), which builds on [0.0.81](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.81) and [0.0.80](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.80). + +### RFC 7662 token introspection and admin bootstrap + +Otoroshi now implements the [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662) introspection flow, so you can validate opaque access tokens against an introspection endpoint. The `OIDCJwtVerifier` also reads the audience from an array, aligning it with providers that emit `aud` as a list. + +The initial admin password can now be generated and stored in a temporary file at first startup, which makes automated and reproducible bootstrap of a fresh instance easier. + +### Hardened API key validation + +Several fixes tighten how API keys are accepted. Disabled API keys are no longer accepted in client-id-only mode, and a bearer signature validation bypass in that same mode has been corrected. Legacy quota declarations are now reported on the API key quotas endpoint, so quota reporting stays consistent across key formats. + +### Elasticsearch 8 and Expression Language + +Writes to Elasticsearch 8 no longer include the deprecated `_type` field, and the cluster version can be auto-filled from the interface. The Expression Language handles default values more reliably and fixes the ordering of `ctx.geolocation.*` and `ctx.useragent.*` expressions. The `NgErrorRewriter` plugin also receives several improvements, and ingress fetching now targets the correct API group. + +### LLM extension: A2A, MCP virtual servers and gateway discovery + +The bundled LLM extension reaches [0.0.82](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.82) and includes the features introduced in 0.0.81 and 0.0.80. The provider and capability catalogs are now exposed through the authenticated admin API, and a new `RampartBodyRedactionBackend` terminal plugin reads the request body, redacts PII with the local Rampart model and returns the redacted body directly. The Mistral provider adopts the OpenAI content representation for chat messages, improving support for structured and multi-part content. + +The extension also gains a self-describing gateway: two endpoints, `GET /providers` and `GET /model-capabilities`, let clients discover available providers and model types at runtime, with filtering across text, audio, image, OCR, embedding, moderation and video capabilities. Both are also available as standalone plugins. Embeddings now work with OpenAI-compatible endpoints and OVH AI Endpoints, and the OpenAI-compatible plugin extends to image, audio and moderation on top of chat and embeddings. + +The 0.0.80 release added bidirectional Agent-to-Agent (A2A) support, so Otoroshi agents can be published as standards-compliant services and consume remote A2A agents as tools. MCP virtual servers bundle tool functions, filtering, OAuth security, rate limiting and Zero-Trust controls into a single persisted entity that can be published to the MCP registry. Zero-Trust controls add anti-rug-pull pinning to detect tool definition changes, along with tool-poisoning and prompt-injection scanning. A local Rampart PII guardrail redacts personally identifiable information offline through an ONNX model, and OAuth hardening brings audience validation ([RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)) and opaque token introspection ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)). An Exa search engine provider and real-time MCP and LLM gateway metrics round out the release. + +You can update through your add-on dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_OTOROSHI_VERSION` of the underlying Java application to `v17.17.0_1783064693` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever otoroshi version check yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId +clever otoroshi version update yourOtoroshiNameOrId v17.17.0_1783064693 +``` + +- [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) From c32a44bd2945b09e7c74d68e713ba9a95369a58a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 10 Jul 2026 13:18:47 +0200 Subject: [PATCH 072/214] changelog: Keycloak 26.6.4 and 26.7.0 --- .../2026/07-10-keycloak-26.6.4-26.7.0.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 content/changelog/2026/07-10-keycloak-26.6.4-26.7.0.md diff --git a/content/changelog/2026/07-10-keycloak-26.6.4-26.7.0.md b/content/changelog/2026/07-10-keycloak-26.6.4-26.7.0.md new file mode 100644 index 000000000..d9655775a --- /dev/null +++ b/content/changelog/2026/07-10-keycloak-26.6.4-26.7.0.md @@ -0,0 +1,32 @@ +--- +title: Keycloak 26.6.4 and 26.7.0 are available (security updates) +description: Keycloak 26.6.4 and 26.7.0 fix twelve vulnerabilities, while 26.7.0 adds SCIM provisioning, multi-cluster improvements and SAML step-up authentication +date: 2026-07-10 +tags: + - addons + - keycloak +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Keycloak 26.6.4](https://github.com/keycloak/keycloak/releases/tag/26.6.4) and [Keycloak 26.7.0](https://github.com/keycloak/keycloak/releases/tag/26.7.0) are available on Clever Cloud. Keycloak 26.6.4 addresses eight security vulnerabilities: [CVE-2026-9099](https://nvd.nist.gov/vuln/detail/CVE-2026-9099), [CVE-2026-9083](https://nvd.nist.gov/vuln/detail/CVE-2026-9083), [CVE-2026-9086](https://nvd.nist.gov/vuln/detail/CVE-2026-9086), [CVE-2026-9705](https://nvd.nist.gov/vuln/detail/CVE-2026-9705), [CVE-2026-9795](https://nvd.nist.gov/vuln/detail/CVE-2026-9795), [CVE-2026-9799](https://nvd.nist.gov/vuln/detail/CVE-2026-9799), [CVE-2026-9800](https://nvd.nist.gov/vuln/detail/CVE-2026-9800) and [CVE-2026-11800](https://nvd.nist.gov/vuln/detail/CVE-2026-11800). + +Keycloak 26.7.0 fixes four additional vulnerabilities: [CVE-2026-9796](https://nvd.nist.gov/vuln/detail/CVE-2026-9796), [CVE-2026-9689](https://nvd.nist.gov/vuln/detail/CVE-2026-9689), [CVE-2026-9798](https://nvd.nist.gov/vuln/detail/CVE-2026-9798) and [CVE-2026-11986](https://nvd.nist.gov/vuln/detail/CVE-2026-11986). It also promotes the SCIM API to preview, adds a preview of simplified multi-cluster high availability without external caches and supports step-up authentication for SAML clients. + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_KEYCLOAK_VERSION` of the underlying Java application to `26.7.0` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever keycloak version check yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId 26.7.0 +``` + +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak) From 5db0c2d52e88aca7b08fb2dc8d83682507bb442e Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 24 Jul 2026 21:42:00 +0200 Subject: [PATCH 073/214] changelog: Redis 8.8.1 --- content/changelog/2026/07-24-redis-8.8.1.md | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 content/changelog/2026/07-24-redis-8.8.1.md diff --git a/content/changelog/2026/07-24-redis-8.8.1.md b/content/changelog/2026/07-24-redis-8.8.1.md new file mode 100644 index 000000000..388225538 --- /dev/null +++ b/content/changelog/2026/07-24-redis-8.8.1.md @@ -0,0 +1,22 @@ +--- +title: Redis 8.8.1 is available (security update) +description: Redis 8.8.1 patches out-of-bounds writes triggered by crafted RESTORE payloads in the Probabilistic and TDigest modules +date: 2026-07-24 +tags: + - addons + - redis +authors: + - name: Aurélien Hébert + link: https://github.com/aurrelhebert + image: https://github.com/aurrelhebert.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated Redis™ to [release 8.8.1](https://github.com/redis/redis/releases/tag/8.8.1). It is a security-only release: crafted `RESTORE` payloads targeting the Probabilistic (RedisBloom) and TDigest data structures could trigger out-of-bounds writes, potentially leading to remote code execution. No other change ships with this version. + +Redis™ 8.8.1 is available for new add-ons. Those already deployed can upgrade through migration. + +- [Learn more about Redis™ on Clever Cloud](/doc/addons/redis/) From 824ebe0283155b6b5c52223521ff988becd7b940 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 29 Jul 2026 14:09:16 +0200 Subject: [PATCH 074/214] changelog: images updates, 2026W31 --- content/changelog/2026/07-28-images-update.md | 56 +++++++++++++++++++ content/doc/applications/static.md | 2 +- data/runtime_versions.yml | 4 +- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 content/changelog/2026/07-28-images-update.md diff --git a/content/changelog/2026/07-28-images-update.md b/content/changelog/2026/07-28-images-update.md new file mode 100644 index 000000000..e0e0c5696 --- /dev/null +++ b/content/changelog/2026/07-28-images-update.md @@ -0,0 +1,56 @@ +--- +title: "Images update: Rust 1.97, Yarn 4.17, Hugo 0.164" +description: All runtimes updated, PHP and Node.js included, with fresh Rust and Yarn releases. Hugo 0.164 is now available for static applications +date: 2026-07-28 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images. Deployment is in progress for all our users. + +* **Common:** + * Chromium 150.0.7871.186 + * Clever Tools 4.11.0 + * Git 2.55.0 + * Nano 9.1 + * NGINX 1.30.4 + * OpenSSH 10.4_p1 + * Perl 5.44.0 + * Poppler 26.07.0 + * Zellij 0.43.1 +* **Docker:** + * Update to 29.6.2 +* **Go:** + * Go 1.26.5 +* **Node.js & Bun:** + * Node.js 24.18.0 (npm 11.16.0) + * Yarn 4.17.1 +* **Java:** + * Update to 11.0.32_p9 + * Update to 21.0.12_p8 +* **PHP:** + * Update to 8.2.32 + * Update to 8.3.32 + * Update to 8.4.23 + * Update to 8.5.8 + * Composer 2.10.2 + * Elastic APM agent 1.17.0 + * New Relic extension 12.8.0.37 +* **Python:** + * uv 0.11.29 +* **Rust:** + * Update to 1.97.1 +* **Static:** + * Caddy 2.11.4 + * Hugo 0.164.0 + +## Linux Kernel + +Kernel is [now updated independently](/changelog/2026/05-12-linux-kernel-7.0.6). Current version is 7.1.5. + diff --git a/content/doc/applications/static.md b/content/doc/applications/static.md index d088e0b03..43cc854e9 100644 --- a/content/doc/applications/static.md +++ b/content/doc/applications/static.md @@ -110,7 +110,7 @@ Supported Static Site Generators (SSG) are: - Detected file: `hugo.toml`, `hugo.yaml`, `hugo.json` > [!TIP] Set the Hugo version ->Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.160`, `0.161` , `0.162` or `0.163` (default). +>Use a specific Hugo version by setting the `CC_HUGO_VERSION` environment variable to `0.160`, `0.161`, `0.162`, `0.163` (default) or `0.164`. ### mdBook diff --git a/data/runtime_versions.yml b/data/runtime_versions.yml index 80b5c7b19..70525e52b 100644 --- a/data/runtime_versions.yml +++ b/data/runtime_versions.yml @@ -6,7 +6,7 @@ bun: caddy: eol_source: https://github.com/caddyserver/caddy/releases default: - - "2.11.2" + - "2.11.4" dotnet: eol_source: https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core @@ -56,7 +56,7 @@ java: node: eol_source: https://nodejs.org/en/about/releases/ default: - - 24.16.0 (npm 11.13.0) + - 24.18.0 (npm 11.6.0) php: eol_source: https://www.php.net/supported-versions.php From f0070a14a1e25871130fcd79a6451ded682a1a60 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 29 Jul 2026 16:07:16 +0200 Subject: [PATCH 075/214] changelog: MySQL 8.0.46 and 8.4.10 --- .../2026/07-29-mysql-8.0.46-8.4.10.md | 23 +++++++++++++++++++ content/doc/addons/mysql.md | 2 +- data/software_versions_shared_dedicated.yml | 6 ++--- 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md diff --git a/content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md b/content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md new file mode 100644 index 000000000..45f3ddf03 --- /dev/null +++ b/content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md @@ -0,0 +1,23 @@ +--- +title: MySQL 8.0.46 and 8.4.10 are available +description: MySQL 8.4.10 ships on Clever Cloud with eight security fixes, alongside 8.0.46, the final Percona Server 8.0 release now that this branch reached its end of life +date: 2026-07-29 +tags: + - addons + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated MySQL images for releases 8.4 (8.4.10-10) and 8.0 (8.0.46-37). They're now available for add-ons creation and migration. + +The 8.4 release addresses eight security vulnerabilities: [CVE-2026-46850](https://nvd.nist.gov/vuln/detail/CVE-2026-46850), [CVE-2026-46860](https://nvd.nist.gov/vuln/detail/CVE-2026-46860), [CVE-2026-46861](https://nvd.nist.gov/vuln/detail/CVE-2026-46861), [CVE-2026-46862](https://nvd.nist.gov/vuln/detail/CVE-2026-46862), [CVE-2026-46863](https://nvd.nist.gov/vuln/detail/CVE-2026-46863), [CVE-2026-46869](https://nvd.nist.gov/vuln/detail/CVE-2026-46869), [CVE-2026-46870](https://nvd.nist.gov/vuln/detail/CVE-2026-46870) and [CVE-2026-46871](https://nvd.nist.gov/vuln/detail/CVE-2026-46871). Only [CVE-2026-46863](https://nvd.nist.gov/vuln/detail/CVE-2026-46863) affects the database server itself, the others concern MySQL Shell, MySQL Router and NDB Cluster. + +MySQL 8.0 [reached its end of life on 30 April 2026](https://www.mysql.com/support/eol-notice.html), and 8.0.46-37 is [the final release of the Percona Server 8.0 series](https://docs.percona.com/new/2026/06/10/percona-server-for-mysql-8046-37-has-been-released/). If you run a MySQL 8.0 add-on, we recommend you migrate it to the 8.4 LTS branch. + +* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +* [Learn more about MySQL 8.0.46](https://docs.percona.com/percona-server/8.0/release-notes/8.0.46-37.html) +* [Learn more about MySQL 8.4.10](https://docs.percona.com/percona-server/8.4/release-notes/8.4.10-10.html) diff --git a/content/doc/addons/mysql.md b/content/doc/addons/mysql.md index 48e2b3fc2..d678df651 100644 --- a/content/doc/addons/mysql.md +++ b/content/doc/addons/mysql.md @@ -23,7 +23,7 @@ MySQL is an open source relational database management system (RDBMS). Clever Cl ## Supported Versions -MySQL is available in regular versions and `early` for 8.4. That means it's the first release (8.4.0) of this long term support (LTS) branch, so you should consider it mostly to make some tests and discover what's new. But we recommend waiting a bit before using a new branch in production. +Use the 8.4 long term support (LTS) branch for new add-ons. MySQL 8.0 [reached its end of life on 30 April 2026](https://www.mysql.com/support/eol-notice.html) and [Percona Server 8.0 had its final release in June 2026](https://docs.percona.com/new/2026/06/10/percona-server-for-mysql-8046-37-has-been-released/), so we recommend you migrate your existing 8.0 add-ons to 8.4. {{< software_versions_shared_dedicated mysql>}} diff --git a/data/software_versions_shared_dedicated.yml b/data/software_versions_shared_dedicated.yml index de6bc9310..e9ff752e1 100644 --- a/data/software_versions_shared_dedicated.yml +++ b/data/software_versions_shared_dedicated.yml @@ -15,10 +15,10 @@ mongo: mysql: dedicated: - 5.7 (EOL) - - 8.0.45 - - 8.4.8 + - 8.0.46 (EOL) + - 8.4.10 dev: - - 8.0.45 + - 8.0.46 (EOL) pg: dedicated: From 8562a9c58d1df8fecc59018f320bb4c62a9d3c2c Mon Sep 17 00:00:00 2001 From: Corentin BARAULT Date: Thu, 18 Jun 2026 14:44:51 +0200 Subject: [PATCH 076/214] addons(postgresql): add pg_partman to the extension that can be installed --- content/doc/addons/postgresql.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/doc/addons/postgresql.md b/content/doc/addons/postgresql.md index a2fbb7945..49c7d795f 100644 --- a/content/doc/addons/postgresql.md +++ b/content/doc/addons/postgresql.md @@ -146,6 +146,7 @@ Extension | Description pg_cron | Job scheduler for PostgreSQL pg_ivm | Incremental view maintenance for PostgreSQL pg_net | Enables asynchronous (non-blocking) HTTP/HTTPS requests with SQL +pg_partman | Extension to manage partitioned tables by time or ID pg_repack | Reorganize tables in PostgreSQL databases with minimal locks pgaudit | Provides detailed session and/or object audit logging via the standard PostgreSQL logging facility pgsql-http | HTTP client for PostgreSQL From f645ede75c5eb07e978c50b7f7008cbe5afcbdbf Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 5 Aug 2026 11:24:18 +0200 Subject: [PATCH 077/214] changelog: images updates, 2026W32 --- content/changelog/2026/08-05-images-update.md | 45 +++++++++++++++++++ data/runtime_versions.yml | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 content/changelog/2026/08-05-images-update.md diff --git a/content/changelog/2026/08-05-images-update.md b/content/changelog/2026/08-05-images-update.md new file mode 100644 index 000000000..2a0daf17e --- /dev/null +++ b/content/changelog/2026/08-05-images-update.md @@ -0,0 +1,45 @@ +--- +title: "Images update: Blackfire agent 2026.7, Yarn 4.18, uv 0.12" +description: Runtime updates for .NET, Java, Node.js and PHP, with Blackfire agent 2026.7, Yarn 4.18 and uv 0.12 +date: 2026-08-05 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images. Deployment is in progress for all our users. + +* **Common:** + * Chromium 151.0.7922.71 + * Redis 8.10.0 + * Ripgrep 15.2.0 +* **.NET:** + * Update to 8.0.129 + * Update to 9.0.119 + * Update to 10.0.110 +* **Java:** + * Update to 1.8.0.502_p07 + * Update to 17.0.20_p8 + * Update to 25.0.4_p7 +* **Node.js & Bun:** + * Node.js 24.18.1 (npm 11.16.0) + * Yarn 4.18.0 +* **PHP:** + * Update to 8.2.33 + * Update to 8.3.33 + * Update to 8.4.24 + * Update to 8.5.9 + * Blackfire agent 2026.7.0 + * Blackfire extension 2026.7.1 +* **Python:** + * pip 26.2 + * uv 0.12.0 + +## Linux Kernel + +Kernel is [now updated independently](/changelog/2026/05-12-linux-kernel-7.0.6). Current version is 7.1.8. diff --git a/data/runtime_versions.yml b/data/runtime_versions.yml index 70525e52b..28f3c73b9 100644 --- a/data/runtime_versions.yml +++ b/data/runtime_versions.yml @@ -56,7 +56,7 @@ java: node: eol_source: https://nodejs.org/en/about/releases/ default: - - 24.18.0 (npm 11.6.0) + - 24.18.1 (npm 11.16.0) php: eol_source: https://www.php.net/supported-versions.php From 6fb661c010e5b9773219b668803666fc26e37ce7 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 7 Aug 2026 18:22:16 +0200 Subject: [PATCH 078/214] changelog: Metabase password reset endpoint blocked --- .../08-07-metabase-security-reset-password.md | 62 +++++++++++++++++++ content/doc/addons/metabase.md | 3 + 2 files changed, 65 insertions(+) create mode 100644 content/changelog/2026/08-07-metabase-security-reset-password.md diff --git a/content/changelog/2026/08-07-metabase-security-reset-password.md b/content/changelog/2026/08-07-metabase-security-reset-password.md new file mode 100644 index 000000000..41cb1c459 --- /dev/null +++ b/content/changelog/2026/08-07-metabase-security-reset-password.md @@ -0,0 +1,62 @@ +--- +title: "Metabase critical security update, password reset endpoint blocked" +description: Metabase 0.58.24, 0.59.21, 0.60.17, 0.61.11, 0.62.9 and 0.63.5 are available on Clever Cloud, requests to /api/session/reset_password are blocked on outdated instances +date: 2026-08-07 +tags: + - addons + - metabase +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Metabase [published a security advisory](https://www.metabase.com/blog/security-update) about a critical vulnerability affecting the `x.58` branch and above. It could be exploited through the unauthenticated `POST /api/session/reset_password` endpoint, which allows an attacker to get an authenticated session on a vulnerable instance. Branches below `x.58` are not affected. Technical details are available in the [GHSA-vwf4-m7j8-wcjf advisory](https://github.com/metabase/metabase/security/advisories/GHSA-vwf4-m7j8-wcjf). + +Metabase versions `0.58.24`, `0.59.21`, `0.60.17`, `0.61.11`, `0.62.9` and `0.63.5` fix this vulnerability. They're available on Clever Cloud (versions starting with `1` for the enterprise edition). Update your add-on as soon as possible, through its dashboard in the [Clever Cloud Console](https://console.clever-cloud.com), by setting `CC_METABASE_VERSION` of the underlying Java application to the latest patch of your branch and rebuilding it, or with [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever metabase version check yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId 0.63 +``` + +## Requests to the vulnerable endpoint are blocked + +As long as your Metabase add-on runs an affected version, Clever Cloud blocks requests to `/api/session/reset_password`. Your instance stays protected against this attack, but users can't ask for a password reset email until you update. Once your add-on runs a patched version, requests reach Metabase again. + +You can control this behavior with the `CC_METABASE_BLOCK_RESET_PASSWORD` environment variable of the underlying Java application. Set it to `true` to keep blocking the endpoint, even on a patched version, or to `false` to allow requests on a version that's not up to date. Restart the application for the change to take effect. Keep the default behavior unless you have a specific reason to change it. + +## Detect an attack in your access logs + +The attack pattern described by Metabase is a call to `POST /api/session/reset_password` answered with a `400` status code, immediately followed by a call to `GET /api/user/current` answered with a `200` status code, from the same IP address. You can look for this sequence in the access logs of the Java application of your Metabase add-on, kept for 7 days: + +```bash +clever accesslogs --app yourMetabaseJavaAppId --since 7d --format json-stream \ + | jq -r 'select(.http.request.path == "/api/session/reset_password" or .http.request.path == "/api/user/current") + | "\(.date) \(.source.ip) \(.http.request.method) \(.http.request.path) \(.http.response.statusCode)"' +``` + +Access logs are also available in the [Clever Cloud Console](https://console.clever-cloud.com). A `200` on `/api/user/current` right after a rejected password reset means the attacker got a valid session on your instance: consider it compromised and apply the steps below. + +## What to do after the update + +Metabase recommends the following actions once your instance runs a patched version: + +- Revoke all active sessions by deleting the rows of the `core_session` table in the PostgreSQL database of your add-on +- Review your API keys and delete the ones you don't recognize +- Review administrator accounts and check that they weren't modified +- Rotate the credentials of every database connected to your Metabase instance +- Review the logs of your data warehouses to detect unauthorized access +- Review Metabase activity and query history to detect unexpected queries or exports + +## Users on an XS Java instance + +If your add-on uses `community-latest` as its `CC_METABASE_VERSION` and still runs on an XS Java instance, you must move to a S instance to update. Metabase requires at least 2 GB of RAM starting for releases after x.59.9, as detailed in [a previous changelog entry](/changelog/2026/05-06-metabase-60-ram/): 1 GB provided by the XS instance is no longer enough for recent versions. Update your add-on to a patched version x.60 or above, it will automatically move to a S instance. + +If you need help with these database actions, contact the [Clever Cloud support team](https://console.clever-cloud.com/ticket-center-choice). + +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) diff --git a/content/doc/addons/metabase.md b/content/doc/addons/metabase.md index 8aa66bbe0..c7b32824a 100644 --- a/content/doc/addons/metabase.md +++ b/content/doc/addons/metabase.md @@ -77,6 +77,9 @@ Once you created your add-on, open the management URL or look for `MB_SITE_URL` ## Password reset +> [!WARNING] +> Requests to `/api/session/reset_password` are blocked on `x.58` and above until you update to a version patching a [critical security vulnerability](/changelog/2026/08-07-metabase-security-reset-password/). Use `CC_METABASE_BLOCK_RESET_PASSWORD=true/false` to control this behavior. + To be able to reset your password, you must have [set up an active SMTP server](#configuring-a-smtp-server) in the `e-mail` section of the administrator settings. You can also do it [using a Mailpace add-on](#using-a-mailpace-add-on). Once done, the forgot password procedure will ask you the user email address and send a reset link to it. If you don't have an active SMTP server configured, there is a manual procedure to get the reset link: From 0bfbaa6423ee0ec2f6ed76f7a64d110256701ef7 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 7 Aug 2026 19:14:52 +0200 Subject: [PATCH 079/214] changelog(metabase): Metabase 63 --- content/changelog/2026/08-07-metabase-63.md | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 content/changelog/2026/08-07-metabase-63.md diff --git a/content/changelog/2026/08-07-metabase-63.md b/content/changelog/2026/08-07-metabase-63.md new file mode 100644 index 000000000..d494e2bb4 --- /dev/null +++ b/content/changelog/2026/08-07-metabase-63.md @@ -0,0 +1,36 @@ +--- +title: "Metabase 63 is available, with treemaps, two-factor authentication and PDF subscriptions" +description: Treemap charts, two-factor authentication, PDF attachments, more Metabot providers, one-step dashboard sharing and more +date: 2026-08-07 +tags: + - addons + - metabase +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +The `x.63` branch of Metabase is now available on Clever Cloud with version `0.63.5`. It introduces treemap charts for hierarchical data, two-factor authentication for users signing in with a password, and PDF attachments in dashboard subscriptions. Two-factor authentication is available with Pro and Enterprise plans. + +Metabot can now use OpenAI, AWS Bedrock and Microsoft Azure models in addition to Anthropic, including on the open source edition with your own API key. This release also adds one-step invitations from dashboards and questions, CSV uploads to Snowflake, custom visualizations in the Modular Embedding SDK, and audit logs for MCP authorizations. The Sample Database now uses SQLite instead of H2, and Metabase introduces a predictable support policy with at least 60 days of support for every version and regular Long Term Support releases. + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_METABASE_VERSION` of the underlying Java application to `0.63` or `1.63` for the enterprise edition (EE) and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever metabase version check yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId 0.63 +``` + +- [Learn more about Metabase 63](https://www.metabase.com/releases/metabase-63) +- [Watch the Metabase 63 video playlist](https://www.youtube.com/playlist?list=PLTC-ts2h37r4) +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) + +{{< youtube id="ZZ-KSyG7OVc" >}} From d88fb61f00e018b3dbde62511da76b3ef5327663 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 11 Aug 2026 17:26:14 +0200 Subject: [PATCH 080/214] changelog: Keycloak 26.7.1 --- .../changelog/2026/08-11-keycloak-26.7.1.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 content/changelog/2026/08-11-keycloak-26.7.1.md diff --git a/content/changelog/2026/08-11-keycloak-26.7.1.md b/content/changelog/2026/08-11-keycloak-26.7.1.md new file mode 100644 index 000000000..be6645606 --- /dev/null +++ b/content/changelog/2026/08-11-keycloak-26.7.1.md @@ -0,0 +1,32 @@ +--- +title: Keycloak 26.7.1 (security update) +description: Keycloak 26.7.1 brings the 26.7 features to Clever Cloud and fixes twelve vulnerabilities affecting OIDC, SAML, LDAP and fine-grained permissions +date: 2026-08-11 +tags: + - addons + - keycloak +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[The release 26.7.1](https://github.com/keycloak/keycloak/releases/tag/26.7.1) of Keycloak is available on Clever Cloud. It includes the new capabilities introduced in [Keycloak 26.7.0](https://github.com/keycloak/keycloak/releases/tag/26.7.0), such as the SCIM API promoted to preview, simplified multi-cluster high availability without external caches, step-up authentication for SAML clients and the experimental Admin API v2 for declarative client management. + +This security update addresses twelve vulnerabilities: [CVE-2026-9793](https://nvd.nist.gov/vuln/detail/CVE-2026-9793), [CVE-2026-4629](https://nvd.nist.gov/vuln/detail/CVE-2026-4629), [CVE-2026-14209](https://nvd.nist.gov/vuln/detail/CVE-2026-14209), [CVE-2026-14614](https://nvd.nist.gov/vuln/detail/CVE-2026-14614), [CVE-2026-14615](https://nvd.nist.gov/vuln/detail/CVE-2026-14615), [CVE-2026-15573](https://nvd.nist.gov/vuln/detail/CVE-2026-15573), [CVE-2026-15572](https://nvd.nist.gov/vuln/detail/CVE-2026-15572), [CVE-2026-16100](https://nvd.nist.gov/vuln/detail/CVE-2026-16100), [CVE-2026-16442](https://nvd.nist.gov/vuln/detail/CVE-2026-16442), [CVE-2026-16443](https://nvd.nist.gov/vuln/detail/CVE-2026-16443), [CVE-2026-16071](https://nvd.nist.gov/vuln/detail/CVE-2026-16071) and [CVE-2026-16102](https://nvd.nist.gov/vuln/detail/CVE-2026-16102). + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_KEYCLOAK_VERSION` of the underlying Java application to `26.7.1` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever keycloak version check yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId 26.7.1 +``` + +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak) From 825c5c71ede3b0bd659c39c6769f694fc942d091 Mon Sep 17 00:00:00 2001 From: vballu Date: Thu, 13 Aug 2026 18:06:05 +0200 Subject: [PATCH 081/214] metrics: remove metrics beta notice --- content/doc/metrics/_index.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/content/doc/metrics/_index.md b/content/doc/metrics/_index.md index 460bea482..6e8d1a578 100644 --- a/content/doc/metrics/_index.md +++ b/content/doc/metrics/_index.md @@ -19,10 +19,6 @@ aliases: - /doc/tools/metrics - /metrics --- -{{< callout type="warning" >}} -Clever Cloud Metrics is still in beta. -{{< /callout >}} - In addition to logs, you can have access to metrics to know how your application behaves. By default, system metrics like CPU and RAM use are available, as well as application-level metrics when available (apache or nginx status for instance). From 301bd45291fe5f95384041c67ab784a1695711a6 Mon Sep 17 00:00:00 2001 From: Corentin BARAULT Date: Thu, 18 Jun 2026 12:15:51 +0200 Subject: [PATCH 082/214] addons(cellar): add a section about pre-signed URL and checksum validation --- content/doc/addons/cellar.md | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/content/doc/addons/cellar.md b/content/doc/addons/cellar.md index 092b36ec1..21747bb28 100644 --- a/content/doc/addons/cellar.md +++ b/content/doc/addons/cellar.md @@ -819,6 +819,110 @@ When versioning is enabled, the newly added object is automatically provided wit {{< /tabs >}} +## Uploading objects with presigned URLs and checksum validation + +Pre-signed URLs allow the client to upload files directly to S3 without ever exposing your credentials. You simply generate a temporary, time-limited URL and the client uses it to upload their file to your cellar. +To ensure the integrity of the uploaded file, you can add a checksum as part of the URL parameters. This checksum acts as a fingerprint for the expected file. + +Cellar supports pre-signed URLs and MD5 checksum validation. If you pre-sign your URL with an MD5 checksum as a parameter, Cellar validates the uploaded file against the expected checksum by verifying both the request headers and the file content. The upload fail if either value doesn't match the expected checksum. + +{{< tabs items="Python, Node.js" >}} + + {{< tab >}} + + ```python + import boto3 + import hashlib + import base64 + from botocore.config import Config + from botocore.exceptions import ClientError + + # --- Configuration Cellar --- + cellar_host = "https://cellar-c2.services.clever-cloud.com" + access_key = "" + secret_key = "" + bucket_name = "" + object_key = "/" + + # --- Client S3 --- + s3 = boto3.client( + "s3", + endpoint_url=cellar_host, + aws_access_key_id=access_key, + aws_secret_access_key=secret_key, + config=Config( + signature_version="s3v4", + s3={"addressing_style": "path"}, + ) + ) + + # MD5 de "hello world" + md5 = base64.b64encode(hashlib.md5(b"hello world").digest()).decode() + + url = s3.generate_presigned_url( + ClientMethod="put_object", + Params={ + "Bucket": bucket_name, + "Key": object_key, + "ContentMD5": md5, + }, + ExpiresIn=3600, + ) + + print(url) + ``` + + {{< /tab >}} + + {{< tab >}} + + ```js + import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; + import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; + import { createHash } from "crypto"; + + // --- Configuration --- + const s3 = new S3Client({ + endpoint: "https://cellar-c2.services.clever-cloud.com", + region: "us-east-1", + credentials: { + accessKeyId: "", + secretAccessKey: "", + }, + forcePathStyle: true, + }); + + const bucket = ""; + const key = "/"; + + // --- Calcul du checksum MD5 (base64) --- + const fileContent = Buffer.from("Hello World"); + const md5Base64 = createHash("md5").update(fileContent).digest("base64"); + + // --- Génération de l'URL présignée --- + const command = new PutObjectCommand({ + Bucket: bucket, + Key: key, + ContentMD5: md5Base64, + }); + + const presignedUrl = await getSignedUrl(s3, command, { + expiresIn: 3600, + // ← Force le header Content-MD5 à rester dans la signature (non hoistable) + unhoistableHeaders: new Set(["content-md5"]), + }); + + console.log("URL présignée :", presignedUrl); + ``` + + {{< /tab >}} + +{{< /tabs >}} + +{{< callout type="info">}} + If you use SHA-256 for the checksum, Cellar verifies that the SHA-256 checksum specified in the URL matches the value provided in the request headers. However, unlike MD5, it doesn't validate the file content against the checksum, meaning data integrity isn't verified during upload. +{{< /callout >}} + ## Troubleshooting {{% details title="SSL error with s3cmd" closed="true" %}} From 9a8de12443cd09dd453be53d4753f03c965f0413 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Sat, 15 Aug 2026 16:34:27 +0200 Subject: [PATCH 083/214] changelog(metabase): add 0.61.18, 0.62.16 and 0.63.13 --- .../08-15-metabase-0.61.18-0.62.16-0.63.13.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 content/changelog/2026/08-15-metabase-0.61.18-0.62.16-0.63.13.md diff --git a/content/changelog/2026/08-15-metabase-0.61.18-0.62.16-0.63.13.md b/content/changelog/2026/08-15-metabase-0.61.18-0.62.16-0.63.13.md new file mode 100644 index 000000000..1aa596941 --- /dev/null +++ b/content/changelog/2026/08-15-metabase-0.61.18-0.62.16-0.63.13.md @@ -0,0 +1,33 @@ +--- +title: "Metabase 0.61.18, 0.62.16 and 0.63.13 are available (security update)" +description: Metabase 0.61.18, 0.62.16 and 0.63.13 are available on Clever Cloud with security hardening for the supported 61, 62 and 63 branches +date: 2026-08-15 +tags: + - addons + - metabase +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Metabase versions `0.61.18`, `0.62.16` and `0.63.13` are now available on Clever Cloud. These patch releases harden security on the supported `x.61`, `x.62` and `x.63` branches and should be applied as soon as possible. Versions starting with `1` provide the same updates for the enterprise edition. + +If you use `latest` as your `CC_METABASE_VERSION`, restart your instance to deploy the latest patched version. If you use a specific branch, update `CC_METABASE_VERSION` of the underlying Java application to its latest patch and rebuild it. + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com), or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever metabase version check yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId 0.63 +``` + +- [Read the Metabase security-focused release announcement](https://www.metabase.com/blog/security-focused-release-announcement-2026-08-12) +- [Read the Metabase 61 changelog](https://www.metabase.com/changelog/61) +- [Read the Metabase 62 changelog](https://www.metabase.com/changelog/62) +- [Read the Metabase 63 changelog](https://www.metabase.com/changelog/63) +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) From 4fcfe81b61fc45df0ad4c05a7ee15dab5d17ade2 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 19 Aug 2026 10:51:36 +0200 Subject: [PATCH 084/214] changelog: images updates, 2026W34 --- content/changelog/2026/08-19-images-update.md | 49 +++++++++++++++++++ data/runtime_versions.yml | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 content/changelog/2026/08-19-images-update.md diff --git a/content/changelog/2026/08-19-images-update.md b/content/changelog/2026/08-19-images-update.md new file mode 100644 index 000000000..4477cafc2 --- /dev/null +++ b/content/changelog/2026/08-19-images-update.md @@ -0,0 +1,49 @@ +--- +title: "Images update: FFmpeg 9, Node.js 24.19, OpenSSH 10.5, rsync 3.5" +description: Runtime updates for Go, Node.js, Python and Ruby, with FFmpeg 9, OpenSSH 10.5, rsync 3.5 and new PDF tooling +date: 2026-08-19 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated all our images. Deployment is in progress for all our users. + +* **Common:** + * Anubis 1.26.2 + * ClamAV 1.5.4 + * Chromium 151.0.7922.137 + * FFmpeg 9.0 + * Nano 9.2 + * OpenSSH 10.5_p1 + * Poppler 26.08.0 + * pdfio 1.6.4 is now available + * qpdf is no longer included + * rsync 3.5.0 +* **Go:** + * Update to 1.26.6 +* **Node.js & Bun:** + * Node.js 24.19.0 (npm 11.17.0) +* **Python:** + * Update to 3.10.21 + * Update to 3.11.16 + * Update to 3.12.14 + * Update to 3.13.15 + * Update to 3.14.7 +* **Ruby:** + * Update to 3.3.12 + * Update to 3.4.10 + * Update to 4.0.6 + +## Health Check and Request Flow improvements + +This release also improves the management of Health Checks and Request Flow across our application images. See the [Request Flow documentation](/doc/develop/request-flow/) to learn how to configure middleware chaining and automatic port allocation. + +## Linux Kernel + +Kernel is [now updated independently](/changelog/2026/05-12-linux-kernel-7.0.6). Current version is 7.1.10. diff --git a/data/runtime_versions.yml b/data/runtime_versions.yml index 28f3c73b9..571deb350 100644 --- a/data/runtime_versions.yml +++ b/data/runtime_versions.yml @@ -56,7 +56,7 @@ java: node: eol_source: https://nodejs.org/en/about/releases/ default: - - 24.18.1 (npm 11.16.0) + - 24.19.0 (npm 11.17.0) php: eol_source: https://www.php.net/supported-versions.php From 6d6a9e88804dc10fd62ca103347249279924f45c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 19 Aug 2026 16:17:38 +0200 Subject: [PATCH 085/214] changelog: Redis 8.10.1 --- content/changelog/2026/08-19-redis-8.10.1.md | 22 ++++++++++++++++++++ data/software_versions_shared_dedicated.yml | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 content/changelog/2026/08-19-redis-8.10.1.md diff --git a/content/changelog/2026/08-19-redis-8.10.1.md b/content/changelog/2026/08-19-redis-8.10.1.md new file mode 100644 index 000000000..4f0118f65 --- /dev/null +++ b/content/changelog/2026/08-19-redis-8.10.1.md @@ -0,0 +1,22 @@ +--- +title: Redis 8.10.1 is available (security update) +description: Redis 8.10.1 introduces compact hashes and new commands, and fixes multiple memory safety issues +date: 2026-08-19 +tags: + - addons + - redis +authors: + - name: Aurélien Hébert + link: https://github.com/aurrelhebert + image: https://github.com/aurrelhebert.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +We updated Redis™ to [release 8.10.1](https://github.com/redis/redis/releases/tag/8.10.1). It includes the features introduced in [Redis™ 8.10](https://github.com/redis/redis/releases/tag/8.10.0), such as compact hashes that reduce memory usage for keys sharing a schema. It also adds `HIMPORT` for high-throughput compact hash insertion, `LMOVEM` and `BLMOVEM` to move multiple list elements, and `SUNIONCARD` and `SDIFFCARD` to return set cardinalities. + +This security release fixes multiple memory safety issues affecting RDB loading, Vector Sets, TLS client certificate authentication and blocked clients. Redis™ 8.10.1 is available for new add-ons. Those already deployed can upgrade through migration. + +- [Learn more about Redis™ on Clever Cloud](/doc/addons/redis/) diff --git a/data/software_versions_shared_dedicated.yml b/data/software_versions_shared_dedicated.yml index e9ff752e1..2e90e282f 100644 --- a/data/software_versions_shared_dedicated.yml +++ b/data/software_versions_shared_dedicated.yml @@ -32,4 +32,4 @@ pg: redis: dedicated: - - v8.8.0 + - v8.10.1 From 62a6503a43e47925381dc5d5dd7adc28c0b610d9 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 21 Aug 2026 10:27:43 +0200 Subject: [PATCH 086/214] changelog(metabase): add 0.61.19, 0.62.17 and 0.63.14 --- .../08-21-metabase-0.61.19-0.62.17-0.63.14.md | 40 +++++++++++++++++++ content/doc/addons/metabase.md | 12 +++--- 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 content/changelog/2026/08-21-metabase-0.61.19-0.62.17-0.63.14.md diff --git a/content/changelog/2026/08-21-metabase-0.61.19-0.62.17-0.63.14.md b/content/changelog/2026/08-21-metabase-0.61.19-0.62.17-0.63.14.md new file mode 100644 index 000000000..d0dbd8c4b --- /dev/null +++ b/content/changelog/2026/08-21-metabase-0.61.19-0.62.17-0.63.14.md @@ -0,0 +1,40 @@ +--- +title: "Metabase 0.61.19, 0.62.17 and 0.63.14 are available (security update)" +description: Metabase 0.61.19, 0.62.17 and 0.63.14 harden security and introduce breaking changes to API parameters, dependency permissions and serialization +date: 2026-08-21 +tags: + - addons + - metabase +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +Metabase versions [0.61.19](https://github.com/metabase/metabase/releases/tag/v0.61.19), [0.62.17](https://github.com/metabase/metabase/releases/tag/v0.62.17) and [0.63.14](https://github.com/metabase/metabase/releases/tag/v0.63.14) are now available on Clever Cloud. These releases focus on hardening Metabase security and include the changes from private patch releases published for each branch. Versions starting with `1` provide the same updates for the enterprise edition. + +## Breaking changes + +These security measures introduce breaking changes. Metabase no longer supports undocumented API usage, and the `/api/card/{card-id}/query/{export-format}` endpoint now requires a non-blank `id` for each item in the `parameters` array. Users also need **View** collection permissions for every question dependency, including models, metrics and nested questions. Database secrets are no longer included in serialization exports, but existing exports containing secrets can still be imported. + +## `community-latest` on XS instances + +All Metabase add-ons that used `community-latest` on an XS Java instance have been pinned to `0.59.9`, the latest version that can start with the 1 GB of RAM provided by this instance size. Metabase `0.60` and later require at least 2 GB of RAM, as detailed in the previous changelog entries about [the new RAM requirements](/changelog/2026/05-06-metabase-60-ram/) and [the availability of Metabase 60](/changelog/2026/05-12-metabase-60/). To upgrade, set `CC_METABASE_VERSION` to `latest` and rebuild the underlying Java application. The update automatically resizes it from XS to S because `latest` resolves to a version newer than `0.60`. Keep `latest` afterward so each new deployment uses the most recent Metabase version available on Clever Cloud. + +Back up your Metabase application database and review your API integrations and collection permissions before updating. If you use `latest` on an S instance or larger, restart your instance to get the latest version. If you use a specific branch, update `CC_METABASE_VERSION` of the underlying Java application to its latest patch and rebuild it. + +You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com), or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever metabase version check yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId +clever metabase version update yourMetabaseNameOrId 0.63 +``` + +- [Read the Metabase 61 changelog](https://www.metabase.com/changelog/61) +- [Read the Metabase 62 changelog](https://www.metabase.com/changelog/62) +- [Read the Metabase 63 changelog](https://www.metabase.com/changelog/63) +- [Learn more about Metabase on Clever Cloud](/doc/addons/metabase/) diff --git a/content/doc/addons/metabase.md b/content/doc/addons/metabase.md index c7b32824a..860207b32 100644 --- a/content/doc/addons/metabase.md +++ b/content/doc/addons/metabase.md @@ -156,16 +156,16 @@ clever metabase version update myMetabase If you use `CC_METABASE_VERSION` it can contain a value that is either a special keyword or a [SemVer](https://semver.org/) version requirement (the only difference with SemVer is that `x.y.z` is interpreted as `=x.y.z` instead of `^x.y.z`.): -- `community-latest` (_default_): use the latest version of the Community Edition (_same as `0`, `0.*`, `^0` or empty_) -- `0.55.1`: use the `0.55.1` version (_same as `=0.55.1`_) -- `0.55`: use the latest available version starting with `0.55` (_same as `^0.55.0`, `~0.55.0`_) +- `latest` (_default_): use the latest version of the Community Edition (_same as `0`, `0.*`, `^0` or empty_) +- `0.63.14`: use the `0.63.14` version (_same as `=0.63.14`_) +- `0.63`: use the latest available version starting with `0.63` (_same as `^0.63.0`, `~0.63.0`_) To update Metabase manually, you **should** restart the Java application without the build cache, using the `re-build and restart` button in the [Console](https://console.clever-cloud.com/) or the `clever restart --without-cache` command of [Clever Tools](/doc/cli/applications/deployment-lifecycle/#restart). The Metabase JAR is stored in the build cache so that no time is wasted re-downloading it every time you restart the application (or it is restarting as part of a scaling event). This also makes the service more resilient: should the download be temporarily failing for any reason, this would not prevent restarting/scaling your add-on. {{< callout type="warning" >}} -**With great power comes great responsibility.** If you choose to fix your add-on to a specific version (for example, `0.55.3`) or a specific "branch" (for example, `0.55`), you must make sure that this version/branch does not become obsolete (new Metabase versions that patch critical security issues may be released but not used in your add-on because you specified otherwise). +**With great power comes great responsibility.** If you choose to fix your add-on to a specific version (for example, `0.63.14`) or a specific "branch" (for example, `0.63`), you must make sure that this version/branch does not become obsolete (new Metabase versions that patch critical security issues may be released but not used in your add-on because you specified otherwise). {{< /callout >}} - [The Atom feed (XML) of latest versions and their changelog](https://cc-metabase.cellar-c2.services.clever-cloud.com/metabase_releases.xml) @@ -175,7 +175,7 @@ The Metabase JAR is stored in the build cache so that no time is wasted re-downl Metabase provides an Enterprise Edition (EE) that offers [more features](https://www.metabase.com/docs/latest/paid-features/) but requires a license key that must be purchased through their website (see the [pricing page](https://www.metabase.com/pricing/)) EE versions are usually released at the same time as Community Edition (CE) versions, starting with a `1` instead of a `0`. -If you wish to deploy an EE version on your Clever Cloud add-on, `CC_METABASE_VERSION` environment variable to either use a fixed version/branch that starts with `1` (for example: `CC_METABASE_VERSION=1.55`) or `CC_METABASE_VERSION=enterprise-latest`. +If you wish to deploy an EE version on your Clever Cloud add-on, `CC_METABASE_VERSION` environment variable to either use a fixed version/branch that starts with `1` (for example: `CC_METABASE_VERSION=1.63`) or `CC_METABASE_VERSION=enterprise-latest`. You must then add your license key in Metabase's settings (see [documentation](https://www.metabase.com/docs/latest/installation-and-operation/activating-the-enterprise-edition#if-youre-self-hosting-metabase)). @@ -243,4 +243,4 @@ Here is how you can do it: 8. _(optional)_ Configure the Java application domain and update your DNS record accordingly; if you use a custom domain, you should also update the `MB_SITE_URL` environment variable (it defines the base URL used by links in Metabase emails, among other things) 9. Start the Java application of your Clever Cloud add-on (without build cache) -If everything seems OK, set `CC_METABASE_VERSION` to the value you wish (for example, `community-latest`) in the Java application of your Clever Cloud add-on and restart it. [Contact the Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) if you need advice or help doing that. +If everything seems OK, set `CC_METABASE_VERSION` to the value you wish (for example, `latest`) in the Java application of your Clever Cloud add-on and restart it. [Contact the Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) if you need advice or help doing that. From 863f97522d179f3d39d069689478af6db7e68728 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 24 Aug 2026 10:00:18 +0200 Subject: [PATCH 087/214] docs: callouts, commits message recommandations --- .github/copilot-instructions.md | 37 ++++++++++++++++++++++++++++----- CLAUDE.md | 26 ++++++++++++++++++++++- CONTRIBUTING.md | 36 ++++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 8 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 74b9c7cd4..f8d541562 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -105,12 +105,19 @@ aliases: - Always specify **current versions** of software/tools - Include **environment variables** with exact names and examples - Provide **command-line examples** with proper syntax -- Use **callouts for important information**: +- Prefer **GitHub-style callouts for important information**. They render in both GitHub and the documentation site: ```markdown - > [!NOTE] Context about new features - > [!TIP] Helpful suggestions - > [!WARNING] Important considerations + > [!NOTE] + > Context about new features. + + > [!TIP] + > Helpful suggestions. + + > [!WARNING] + > Important considerations. ``` +- Use the Hugo `{{< callout >}}` shortcode only when GitHub-style syntax can't provide the required rendering or behaviour +- Limit callouts to one or two per page ### Guide-Specific Rules @@ -223,7 +230,7 @@ clever keycloak version update yourKeycloakNameOrId - [ ] Examples use realistic project names and values - [ ] No first-person pronouns (I, we, us, our) - [ ] Short, clear sentences under 25 words -- [ ] Proper callouts for important information +- [ ] GitHub-style callouts used for important information where needed ### Before Publishing Changelog - [ ] Clear benefit/impact stated upfront @@ -277,6 +284,26 @@ The site is configured for Clever Cloud hosting with the `static` runtime and th ## Quality Assurance Requirements +### Commit Message Convention +Use `section(page): commit message` for content updates. The section and page must identify the documentation area being changed: + +```text +addons(postgresql): document pg_partman support +applications(nodejs): clarify pnpm configuration +changelog(metabase): announce 0.63.14 security update +``` + +Use standard Conventional Commits for documentation structure, Hugo configuration or templates, deployment, CI, tooling, and dependency changes: + +```text +feat(hugo): add a shortcode for version tables +fix(ci): run Vale on shared content +refactor(layouts): simplify changelog rendering +chore(deps): update the Hextra theme +``` + +Split content and structural changes into separate commits when possible. + ### Build Verification Always test changes with the `hugo` command before committing to ensure the build is functional. Fix any build errors immediately as they prevent deployment. Verify that all links, references, image paths, and shortcode syntax work correctly in the generated output. diff --git a/CLAUDE.md b/CLAUDE.md index 6f15fe59a..063018b21 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,6 +78,30 @@ For changelog entries, also include: - **Structure**: Use 2-4 well-developed paragraphs per section, minimize bullet lists - **Paragraphs**: Aim for 3-6 lines for optimal readability +### Callouts +- Prefer GitHub-style callouts because they render in both GitHub and the documentation site: + ```markdown + > [!NOTE] + > This information helps readers understand the current behaviour. + + > [!WARNING] + > Back up your application database before upgrading. + ``` +- Use the Hugo `{{< callout >}}` shortcode only when GitHub-style syntax can't provide the required rendering or behaviour +- Limit callouts to one or two per page + +### Commit Messages +- For content updates, use `section(page): commit message`, for example: + - `addons(postgresql): document pg_partman support` + - `applications(nodejs): clarify pnpm configuration` + - `changelog(metabase): announce 0.63.14 security update` +- For documentation structure, Hugo, deployment, CI, tooling, or dependency changes, use standard Conventional Commits, for example: + - `feat(hugo): add a shortcode for version tables` + - `fix(ci): run Vale on shared content` + - `refactor(layouts): simplify changelog rendering` + - `chore(deps): update the Hextra theme` +- Split content and structural changes into separate commits when possible + ### Code and Technical Examples - Always provide complete, runnable code examples - Use exact environment variable names: `CC_WEBROOT`, `CC_NODE_BUILD_TOOL`, etc. @@ -103,7 +127,7 @@ Runtime versions and software compatibility information is maintained in `/data/ - `{{% steps %}}` - Create step-by-step instructions for guides - `{{< tabs items="npm,yarn,pnpm" >}}` - Create tabbed content sections - `{{< cards >}}` - Display card layouts for related resources -- `{{< callout >}}` - Create note, tip, warning callouts +- `{{< callout >}}` - Create a callout only when GitHub-style syntax isn't sufficient - `{{< hextra/hero-subtitle >}}` - Add engaging subtitles in guides ### Hugo Content Types diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 71c384682..ddd345641 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,13 +40,24 @@ Sources for quality content are currently being updated. - Using phrases like _simply_, _It's that simple_, _It's easy_, or _quickly_ in a procedure. - Over-politeness with the use of _please_: go straight to the point. -#### 💡 Shortcodes +#### 💡 Shortcodes and callouts This doc uses Hugo with [Hextra theme](https://imfing.github.io/hextra/), which provides a variety of [shortcodes](https://imfing.github.io/hextra/docs/guide/shortcodes/) to enhance it and improve its readability. For example : - [Steps](https://imfing.github.io/hextra/docs/guide/shortcodes/steps/) are well suited for the `/guides/` section, or for any tutorial. -- [Callouts](https://imfing.github.io/hextra/docs/guide/shortcodes/callout/) draw attention to an important information in the page. However, don't overuse them, as too many callouts can miss their point and make the page crowded. Limit callouts to one or two per page. + +Use GitHub-style callouts whenever possible. They remain readable on GitHub and don't depend on Hugo-specific rendering: + +```markdown +> [!NOTE] +> This information helps readers understand the current behaviour. + +> [!WARNING] +> Back up your application database before upgrading. +``` + +Use the [Hextra callout shortcode](https://imfing.github.io/hextra/docs/guide/shortcodes/callout/) only when a GitHub-style callout can't provide the required rendering or behaviour. Don't overuse callouts: limit them to one or two per page. ### 💅 Style guide @@ -68,6 +79,27 @@ Priority goes to PRs that reference a problem addressed in an issue fitting the - **Keep it small:** The quality of the review is inversely proportional to the size of the PR. Smaller PRs simplify the reviewing process and increase the chances of getting constructive feedback. - **Accept the feedback:** If reviewers ask you to make changes, do it. If you disagree, explain why. If you aren't sure, ask for clarification. Don't nitpick on the feedback, and don't take it personally. +#### Commit messages + +For content updates, use `section(page): commit message`. The section and page identify the documentation area you changed: + +```text +addons(postgresql): document pg_partman support +applications(nodejs): clarify pnpm configuration +changelog(metabase): announce 0.63.14 security update +``` + +For changes to the documentation structure, Hugo configuration or templates, deployment, CI, tooling, or dependencies, use the standard Conventional Commits format `type(scope): commit message`: + +```text +feat(hugo): add a shortcode for version tables +fix(ci): run Vale on shared content +refactor(layouts): simplify changelog rendering +chore(deps): update the Hextra theme +``` + +Keep content and structural changes in separate commits when possible so each commit can follow the appropriate convention. + ### 🥸 When reviewing a PR - **Latency:** Long PR review latency can be disappointing for the authors, and make merge conflicts arise in their branch. Long latency kills productivity and morale, so make sure to review PRs in a timely manner. From fd0306d42b1ff7574326d590bc7d38a6f0fd7b9a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 24 Aug 2026 08:14:37 +0200 Subject: [PATCH 088/214] changelog(keycloak): Keycloak 26.7.2 --- .../changelog/2026/08-24-keycloak-26.7.2.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 content/changelog/2026/08-24-keycloak-26.7.2.md diff --git a/content/changelog/2026/08-24-keycloak-26.7.2.md b/content/changelog/2026/08-24-keycloak-26.7.2.md new file mode 100644 index 000000000..4ca5e8daa --- /dev/null +++ b/content/changelog/2026/08-24-keycloak-26.7.2.md @@ -0,0 +1,42 @@ +--- +title: "Keycloak critical security update, 26.7.2 is available" +description: Keycloak 26.7.2 fixes CVE-2026-18963, a critical unauthenticated account takeover vulnerability in the password reset flow +date: 2026-08-24 +tags: + - addons + - keycloak +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[Keycloak 26.7.2](https://github.com/keycloak/keycloak/releases/tag/26.7.2) is available on Clever Cloud. It fixes [CVE-2026-18963](https://access.redhat.com/security/cve/CVE-2026-18963), a critical vulnerability with a CVSS score of 9.1. An unauthenticated attacker can bypass the email verification step of the password reset flow, set new credentials for any user and take control of their account without user interaction. + +## Update or temporarily disable password resets + +Update every Keycloak add-on to version 26.7.2 as soon as possible. You can update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_KEYCLOAK_VERSION` of the underlying Java application to `26.7.2` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever keycloak version check yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId --target 26.7.2 +``` + +If you can't update immediately, temporarily disable password resets in every realm. In the Keycloak Admin Console, select a realm, open **Realm settings**, then **Login**, switch **Forgot password** to **Off** and repeat for every realm. This prevents legitimate users from requesting a password reset; re-enable the feature only after the instance runs version 26.7.2. + +If you suspect that an account was compromised before the update, reset its credentials, revoke its active sessions and review its recent activity, roles and permissions for unexpected changes. + +## Other security fixes + +Keycloak 26.7.2 addresses seven other vulnerabilities: [CVE-2026-45292](https://nvd.nist.gov/vuln/detail/CVE-2026-45292), [CVE-2026-14613](https://nvd.nist.gov/vuln/detail/CVE-2026-14613), [CVE-2026-59888](https://nvd.nist.gov/vuln/detail/CVE-2026-59888), [CVE-2026-59889](https://nvd.nist.gov/vuln/detail/CVE-2026-59889), [CVE-2026-15945](https://nvd.nist.gov/vuln/detail/CVE-2026-15945), [CVE-2026-17048](https://nvd.nist.gov/vuln/detail/CVE-2026-17048) and [CVE-2026-15571](https://nvd.nist.gov/vuln/detail/CVE-2026-15571). They cover account takeover risks in account linking flows, permission bypasses and information disclosure in fine-grained admin permissions, exposure of rotated client secrets, unbounded memory allocation in OpenTelemetry baggage processing and vulnerabilities in Jackson Databind. The release also prevents `show-config` from displaying the Vault keystore password in clear text and fixes bugs affecting SCIM, OIDC, WebAuthn, stateless clusters and the Admin UI. + +- [Read the CVE-2026-18963 advisory](https://access.redhat.com/security/cve/CVE-2026-18963) +- [Read the Keycloak 26.7.2 release notes](https://www.keycloak.org/2026/08/keycloak-2672-released) +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak) From 3c2849abf65dd55bfa3231782d8b9405eb4404ff Mon Sep 17 00:00:00 2001 From: hcaumeil Date: Fri, 21 Aug 2026 22:49:02 +0200 Subject: [PATCH 089/214] reference(env vars): update Node.js build tools --- content/doc/reference/reference-environment-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/doc/reference/reference-environment-variables.md b/content/doc/reference/reference-environment-variables.md index 66c2d0c22..0cedc10b5 100644 --- a/content/doc/reference/reference-environment-variables.md +++ b/content/doc/reference/reference-environment-variables.md @@ -262,7 +262,7 @@ Use Linux runtime with [Mise package manager](#install-tools-with-mise-package-m |[`CC_NODE_VERSION`](/doc/applications/nodejs#set-nodejs-version)| Set Node.js version, for example `24`, `23.11` or `22.15.1` | | |`CC_NODE_DEV_DEPENDENCIES` | Control if development dependencies are installed or not. Values are either `install` or `ignore` | `ignore` | |`CC_RUN_COMMAND` | Define a custom command. Example for Meteor: `node .build/bundle/main.js ` | | -|`CC_NODE_BUILD_TOOL` | Choose your build tool between npm, npm-ci, yarn, yarn2 and custom | npm | +|`CC_NODE_BUILD_TOOL` | Choose your build tool between bun, npm, npm-ci, pnpm, yarn, yarn2, yarn-berry and custom | npm | |`CC_CUSTOM_BUILD_TOOL`| A custom command to run (with `CC_NODE_BUILD_TOOL` set to `custom`) | | |`CC_NPM_REGISTRY` | The host of your private repository, available values: `github` or the registry host. | registry.npmjs.org | |`CC_NPM_BASIC_AUTH`| Private repository credentials, in the form `user:password`. You can't use this if `CC_NPM_TOKEN` is set | | From 90e424a5776ca1d614dc57e3b38b24756ea84b5a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 24 Aug 2026 11:23:51 +0200 Subject: [PATCH 090/214] guides(astro): use native pnpm build tool --- content/guides/astro.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/content/guides/astro.md b/content/guides/astro.md index eb2f0cd5d..d7594934c 100644 --- a/content/guides/astro.md +++ b/content/guides/astro.md @@ -87,9 +87,8 @@ Depending on your package manager, use the following environment variables: {{< /tab >}} {{< tab name="pnpm" icon="pnpm" >}} ```shell - CC_NODE_BUILD_TOOL="custom" - CC_PRE_BUILD_HOOK="npm install -g pnpm && pnpm install" - CC_CUSTOM_BUILD_TOOL="pnpm run astro telemetry disable && pnpm build" + CC_NODE_BUILD_TOOL="pnpm" + CC_POST_BUILD_HOOK="pnpm run astro telemetry disable && pnpm build" CC_RUN_COMMAND="pnpm run preview" ``` {{< /tab >}} From 1e58d3b09c600a74a9551dc85401c3d52b9b6917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Brunat?= Date: Wed, 19 Aug 2026 20:35:12 +0200 Subject: [PATCH 091/214] develop: List the missing pages on the section index The Develop section index only linked five of its nine pages. Clever Tasks, Request Flow, Varnish and Network Groups were reachable through the sidebar and search only. --- content/doc/develop/_index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/doc/develop/_index.md b/content/doc/develop/_index.md index b6ea2f89b..1bb08f165 100644 --- a/content/doc/develop/_index.md +++ b/content/doc/develop/_index.md @@ -20,5 +20,9 @@ aliases: {{< card link="/developers/doc/reference/reference-environment-variables" title="Environment variables reference" icon="creds" >}} {{< card link="/developers/doc/develop/env-variables" title="How environment variables work" icon="question-mark-circle" >}} {{< card link="/developers/doc/develop/workers" title="Workers" icon="arrow-path" >}} + {{< card link="/developers/doc/develop/tasks" title="Clever Tasks" icon="play-circle" >}} {{< card link="/developers/doc/develop/healthcheck" title="Deployment healthcheck path" icon="check" >}} + {{< card link="/developers/doc/develop/request-flow" title="Request Flow" icon="traffic-light" >}} + {{< card link="/developers/doc/develop/varnish" title="Varnish as HTTP cache" icon="arrow-trending-up" >}} + {{< card link="/developers/doc/develop/network-groups" title="Network Groups" icon="tcp-ip-service" >}} {{< /cards >}} From d470da36443c5ce48de02de2b4eceb94fa7f8379 Mon Sep 17 00:00:00 2001 From: Mehdi <26483210+mehdi653@users.noreply.github.com> Date: Thu, 13 Aug 2026 00:07:28 +0000 Subject: [PATCH 092/214] applications(java): document Java 21 as the current default --- content/doc/find-help/faq.md | 2 +- data/runtime_versions.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content/doc/find-help/faq.md b/content/doc/find-help/faq.md index 940d03489..4114faf5b 100644 --- a/content/doc/find-help/faq.md +++ b/content/doc/find-help/faq.md @@ -139,7 +139,7 @@ As an example, if a Spring Boot application was compiled with Java `17` and run java.lang.UnsupportedClassVersionError: org/springframework/boot/loader/JarLauncher has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0 ``` -By default, Java apps on Clever Cloud use Java `11`, but you can change it. Please head [over here](/doc/applications/java/java-jar/#available-java-versions "Java versions") for more information. +By default, Java apps on Clever Cloud use Java `21`, but you can change it. Please head [over here](/doc/applications/java/java-jar/#available-java-versions "Java versions") for more information. For reference, the table below lists the class file version for each major Java version ([official doc](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html)) : diff --git a/data/runtime_versions.yml b/data/runtime_versions.yml index 571deb350..f01eb91e9 100644 --- a/data/runtime_versions.yml +++ b/data/runtime_versions.yml @@ -41,7 +41,7 @@ frankenphp: java: eol_source: https://adoptium.net/fr/support/ default: - - 11 + - 21 accepted: - 25 - 24 (EOL) From 8c7356912ef6577f3ea3df4ceb9bf6c04938211e Mon Sep 17 00:00:00 2001 From: Mehdi <26483210+mehdi653@users.noreply.github.com> Date: Thu, 13 Aug 2026 00:06:59 +0000 Subject: [PATCH 093/214] find-help(faq): update regions, runtimes, flavors and Kubernetes --- content/doc/find-help/faq.md | 40 +++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/content/doc/find-help/faq.md b/content/doc/find-help/faq.md index 4114faf5b..d8faa9391 100644 --- a/content/doc/find-help/faq.md +++ b/content/doc/find-help/faq.md @@ -16,9 +16,9 @@ aliases: ## What is a Scaler? -A _scaler_ is an individual instance hosting your app. You can attribute one or more scalers to your apps. scalers come in many sizes based on each language requirements from pico to XL. +A _scaler_ is an individual instance hosting your application. You can allocate one or more scalers to each application. Available flavors are `pico`, `nano`, `XS`, `S`, `M`, `L`, `XL`, `2XL` and `3XL`, depending on the runtime. -A fixed set of resources supports each scaler. +Each scaler flavor provides a fixed set of resources. When enabling auto-scalability, you have to set a minimum and a maximum of active scalers in your apps settings. This way you can precisely control your monthly fee. @@ -26,19 +26,27 @@ When enabling auto-scalability, you have to set a minimum and a maximum of activ Nano and pico instances operate with **reduced CPU priority** on the host system. As a result, during periods of high load on the hypervisor, these instances may experience performance degradation (since they yield processing power to higher-priority workloads). {{< /callout >}} -## What languages and frameworks are supported by Clever Cloud? -Currently Clever Cloud supports: +## Which application runtimes are supported by Clever Cloud? +Currently, Clever Cloud supports: -* Java (Play Framework 1 & 2, Maven, War files… ) -* Node.js -* PHP ([see frameworks and CMS](/guides)) -* Python (Django) -* Ruby +* .NET +* Docker +* Elixir * Go * Haskell -* Scala +* Java (Jar, Maven, Gradle, War/Ear, Play Framework 1 & 2) and Groovy with Gradle +* Linux +* Meteor.js +* Node.js & Bun +* PHP and FrankenPHP +* Python +* Ruby * Rust -* Docker +* Scala (SBT, Play Framework 1 & 2) +* Static sites (with or without Apache) +* V (Vlang) + +See the full list in the [applications documentation](/doc/applications/), or browse the [guides](/guides/) for frameworks and services you can deploy on Clever Cloud. ## How many applications can I create? As many as you want. We've not set a limited number of apps by developer. @@ -157,20 +165,19 @@ Clever Cloud does not give you access to a server or a VPS, it makes your applic If however, you still need SSH access for debugging purposes, please have a look at [SSH access](/doc/cli/applications/deployment-lifecycle/#ssh), but keep in mind that changes made on an instance are not persistent across deployments. -## I want to user Clever Cloud on my own premises, is that possible? +## I want to use Clever Cloud on my own premises, is that possible? Yes, since 2016 Clever Cloud is packaged for private data center. This offer called "Clever Cloud On Premises" is available upon request: you can send a mail to [sales@clever-cloud.com](mailto:sales@clever-cloud.com) or visit [https://www.clever.cloud/on-premises](https://www.clever.cloud/on-premises) for more info. ## Where are my applications and add-ons located? -Applications and add-ons are located in either _Paris, France_ or _Montreal, Canada_. You can choose where you want it to be when you create an application -and a Clever Cloud add-on. +Applications and add-ons are deployed in multiple regions across Europe, North America and Asia-Pacific, including Paris, Roubaix, Gravelines, London, Warsaw, Montreal, Singapore and Sydney, plus HDS-certified zones in France for healthcare data. When creating an application or an add-on, you can choose among the deployment zones available for that product. Clever Cloud is based in Nantes, France. -## I want to run Kubernetes on top of Clever CLoud, is that possible? +## I want to run Kubernetes on top of Clever Cloud, is that possible? -It's currently not possible to use Kubernetes on our platform. It is however on our Roadmap. +Yes. [Clever Kubernetes Engine](/doc/kubernetes/) (CKE), currently in public beta, provides a managed Kubernetes control plane. See the [product page](https://www.clever.cloud/product/kubernetes/) for more information. ## How to setup a firewall on Clever Cloud? @@ -229,4 +236,3 @@ DEV plan is a free-tier plan available for some databases, designed to let custo Some features such as extensions, simultaneous connections numbers, functions… might be reduced or unavailable. Support is not able to provide help in case of DEV plan. - From 836a0e4f2c42f93b0a1c799a40bc4bd588077303 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 24 Aug 2026 14:47:51 +0200 Subject: [PATCH 094/214] addons(keycloak): remove legacy mention --- content/doc/addons/keycloak.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/content/doc/addons/keycloak.md b/content/doc/addons/keycloak.md index 6bafd8f74..32077077f 100644 --- a/content/doc/addons/keycloak.md +++ b/content/doc/addons/keycloak.md @@ -47,13 +47,6 @@ When you create the Keycloak add-on, Clever Cloud automatically deploys: - A [PostgreSQL](/doc/addons/postgresql) database - A [FS Bucket](/doc/addons/fs-bucket) used for themes, plugins, and import/export storage needs -## Security and updates -Since the Keycloak add-on is a fully managed application, you don't have to select a particular version. It's automatically upgraded and updated both for features and security. - -An add-on update might require a rebuild. - -> Required actions are notified by email - ## Plan sizing By default, Keycloak on Clever Cloud uses small-size resources, i.e: From 371bfa067764cc9ef7ee1440b6da27b2075ce092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20SABLONNI=C3=88RE?= Date: Wed, 24 Jun 2026 17:31:43 +0200 Subject: [PATCH 095/214] administrate(log-management): update access log formats for Clever Tools 4.11 --- content/doc/administrate/log-management.md | 107 ++++++++++++--------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/content/doc/administrate/log-management.md b/content/doc/administrate/log-management.md index da653c0e9..11426da47 100644 --- a/content/doc/administrate/log-management.md +++ b/content/doc/administrate/log-management.md @@ -57,15 +57,7 @@ clever logs --addon ### Access logs -It contains all incoming requests to your application. Here is an example: - -```txt -255.255.255.255 - - [06/Feb/2020:07:59:22 +0100] "GET /aget/to/your/beautiful/website -" 200 1453 -``` - -They are available in different formats, the most common is CLF which stands for Common Log Format. - -You can see access logs with the following command: +It contains all incoming HTTP requests to your application. You can see access logs with the following command: ```bash clever accesslogs @@ -74,48 +66,67 @@ clever accesslogs As with the `logs` command, you can specify `--before` and `--after` flags. If you don't specify any options, the logs display continuously. -To change the output, specify the `--format` flag with one of these values: +To change the output, specify the `--format` (`-F`) flag with one of these values: -- simple: `2021-06-25T10:11:35.358Z 255.255.255.255 GET /` -- extended: `2021-06-25T10:11:35.358Z [ 255.255.255.255 - Nantes, FR ] GET www.clever.cloud / 200` -- clf: `255.255.255.255 - - [25/Jun/2021:12:11:35 +0200] "GET / -" 200 562` -- json: +- `human` (default): a human-readable, colored table + + ```txt + 2026-06-24T08:05:43.880Z 255.255.255.255 FR/Nantes 200 GET / + ``` + +- `clf`: [Common Log Format](https://en.wikipedia.org/wiki/Common_Log_Format) + + ```txt + 255.255.255.255 - - [24/Jun/2026:08:05:43 +0000] "GET /" 200 562 + ``` + + The HTTP protocol version isn't part of the access log payload, so the request line is limited to `method path` (no `HTTP/x.y` token). + +- `json`: a JSON array of log objects (requires a bounding flag such as `--before`) +- `json-stream`: one JSON log object per line + + Both JSON formats share the same object shape: ```json - { - "t":"2021-06-25T10:11:35.358209Z", - "a":"app_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "adc":"clevercloud-adc-nX", - "o":"orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "i":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "ipS":"255.255.255.255", - "pS":58477, - "s":{ - "lt":50.624, - "lg":3.0511, - "ct":"Nantes", - "co":"FR" - }, - "ipD":"46.252.181.17", - "pD":14001, - "d":{ - "lt":45.7059, - "lg":4.7444, - "ct":"Chaponost", - "co":"FR" - }, - "vb":"GET", - "path":"/", - "bIn":658,"bOut":562, - "h":"www.clever.cloud", - "rTime":"31ms", - "sTime":"75μs", - "scheme":"HTTPS", - "sC":200,"sT":"OK", - "w":"WRK-01", - "r":"01F91AEG8Z9RJKYB7JY7H56FNB", - "tlsV":"TLS1.3" - } + { + "id": "01F91AEG8Z9RJKYB7JY7H56FNB", + "date": "2026-06-24T08:05:43.880Z", + "applicationId": "app_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "instanceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "region": "par", + "zone": "par", + "requestId": "01F91AEG8Z9RJKYB7JY7H56FNB", + "bytesIn": 658, + "bytesOut": 562, + "source": { + "ip": "255.255.255.255", + "port": 58477, + "city": "Nantes", + "countryCode": "FR", + "geoLocation": { "latitude": 50.624, "longitude": 3.0511 } + }, + "destination": { + "ip": "46.252.181.17", + "port": 14001, + "city": "Chaponost", + "countryCode": "FR", + "geoLocation": { "latitude": 45.7059, "longitude": 4.7444 } + }, + "http": { + "request": { + "method": "GET", + "path": "/", + "host": "www.clever-cloud.com", + "scheme": "https" + }, + "response": { + "statusCode": 200, + "serviceTime": null, + "time": 31 + } + }, + "tls": null + } ``` ## Exporting logs to an external tool From adacdfacc56f809fd8527ff59665872b6d06d413 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 24 Aug 2026 12:05:10 +0200 Subject: [PATCH 096/214] reference(cli): update Clever Tools commands reference --- content/doc/reference/cli.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/content/doc/reference/cli.md b/content/doc/reference/cli.md index d86e9b68a..f296e8ee3 100644 --- a/content/doc/reference/cli.md +++ b/content/doc/reference/cli.md @@ -279,7 +279,7 @@ clever accesslogs [options] -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) --before, --until Fetch logs before this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h) --F, --format Output format (human, json, json-stream) (default: human) +-F, --format Output format (human, json, json-stream, clf) (default: human) ``` ## activity @@ -1075,6 +1075,30 @@ clever drain [options] -F, --format Output format (human, json) (default: human) ``` +### drain check + +**Description:** Check that a drain's recipient is reachable and accepts deliveries + +**Since:** 4.11.0 + +**Usage** +``` +clever drain check [options] +``` + +**Arguments** +``` +drain-id Drain ID +``` + +**Options** +``` + --addon Add-on ID or real ID +-a, --alias Short name for the application + --app Application to manage by its ID (or name, if unambiguous) +-F, --format Output format (human, json) (default: human) +``` + ### drain create **Description:** Create a drain @@ -1088,7 +1112,7 @@ clever drain create [options] **Arguments** ``` -drain-type Drain type (datadog, elasticsearch, newrelic, ovh-tcp, raw-http, syslog-tcp, syslog-udp) +drain-type Drain type (betterstack, datadog, elasticsearch, newrelic, ovh-tcp, raw-http, syslog-tcp, syslog-udp) drain-url Drain URL ``` @@ -1101,6 +1125,7 @@ drain-url Drain URL -i, --index-prefix Optional index prefix (for elasticsearch), `logstash` value is used if not set -p, --password Basic auth password (for elasticsearch or raw-http) -s, --sd-params RFC5424 structured data parameters (for ovh-tcp), e.g.: `X-OVH-TOKEN=\"REDACTED\"` +-t, --source-token Source token (for betterstack) -u, --username Basic auth username (for elasticsearch or raw-http) ``` From ccbc2846199b5e77b66c92441829c464104676b2 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 24 Aug 2026 12:05:54 +0200 Subject: [PATCH 097/214] administrate(log-management): clarify JSON stream output --- content/doc/administrate/log-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/doc/administrate/log-management.md b/content/doc/administrate/log-management.md index 11426da47..959860278 100644 --- a/content/doc/administrate/log-management.md +++ b/content/doc/administrate/log-management.md @@ -83,7 +83,7 @@ To change the output, specify the `--format` (`-F`) flag with one of these value The HTTP protocol version isn't part of the access log payload, so the request line is limited to `method path` (no `HTTP/x.y` token). - `json`: a JSON array of log objects (requires a bounding flag such as `--before`) -- `json-stream`: one JSON log object per line +- `json-stream`: a stream of JSON log objects Both JSON formats share the same object shape: From 2f4e9b9ce2cb229fd72f35f36e613260e8f8ea4e Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 10 Jun 2026 08:32:00 +0200 Subject: [PATCH 098/214] changelog: Clever Kubernetes Operator 0.8.0 --- .../2026/06-09-kubernetes-operator-0.8.0.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 content/changelog/2026/06-09-kubernetes-operator-0.8.0.md diff --git a/content/changelog/2026/06-09-kubernetes-operator-0.8.0.md b/content/changelog/2026/06-09-kubernetes-operator-0.8.0.md new file mode 100644 index 000000000..559aac99d --- /dev/null +++ b/content/changelog/2026/06-09-kubernetes-operator-0.8.0.md @@ -0,0 +1,20 @@ +--- +title: Clever Kubernetes Operator v0.8.0 is available +date: 2026-06-09 +tags: + - kubernetes + - operator +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 + - name: Gilles Biannic + link: https://github.com/GillesBIANNIC + image: https://github.com/GillesBIANNIC.png?size=40 +description: The operator gets a new name, an updated Kubernetes stack and refreshed documentation +excludeSearch: true +--- + +[Clever Kubernetes Operator v0.8.0](https://github.com/CleverCloud/clever-kubernetes-operator/releases/tag/v0.8.0) is available. The project, formerly known as `clever-operator`, is now named `clever-kubernetes-operator` to better reflect its purpose. This release updates the Kubernetes stack, the `schemars` library and [clevercloud-sdk-rust v1.0.1](https://github.com/CleverCloud/clevercloud-sdk-rust/releases/tag/v1.0.1). Documentation now includes a dedicated credentials section covering the available authentication options, and reflects current add-on versions. + +- [Learn more about Clever Kubernetes Operator](/guides/kubernetes-operator) From b421e1be7aba87eb64e06e4bec87bab07c909795 Mon Sep 17 00:00:00 2001 From: Julie POUNY Date: Thu, 21 May 2026 16:04:21 +0200 Subject: [PATCH 099/214] addons(databases): document custom server configuration --- content/doc/addons/elastic.md | 14 +++++++------- content/doc/addons/mongodb.md | 10 +++++----- content/doc/addons/mysql.md | 10 +++++----- content/doc/addons/postgresql.md | 10 +++++----- content/doc/addons/redis.md | 10 +++++----- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/content/doc/addons/elastic.md b/content/doc/addons/elastic.md index 42bb17741..6e5476788 100644 --- a/content/doc/addons/elastic.md +++ b/content/doc/addons/elastic.md @@ -187,16 +187,16 @@ It's important here to set `number_of_replicas` to zero to avoid triggering clus ## 🔑 Rights and permissions -Elastic Stack add-ons are **managed services**, with Clever Cloud in charge of configuring and maintaining native configuration files. Some operations like adding an oauth source to connect to Kibana can't be added, as well as some native settings modifications. This ensures optimal performances and security for managed services as configured by Clever Cloud. +Elastic Stack add-ons are **managed services**. Clever Cloud configures and maintains their native configuration files. You can change most settings from Kibana or by API, but some native settings, such as adding an OAuth source to Kibana, aren't available directly. This ensures optimal performance and security. -Most settings are available for modifications and update from Kibana or by API, for example: +Settings you can change include: -- Managing users permissions -- Frequencies of back-ups -- The lifecycle of backups indexes -- Backups destination +- Manage user permissions +- Set backup frequency +- Manage the lifecycle of backup indexes +- Configure the backup destination -If you think your system might require some customization (like some plugins activation), contact Clever Cloud support to explain your use case and we will work with you to find a solution. +If your use case requires changing a native configuration file or another unavailable setting, contact [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to discuss feasibility. ## Migrations and upgrades diff --git a/content/doc/addons/mongodb.md b/content/doc/addons/mongodb.md index 6b8ba9bf7..afa7a8a4c 100644 --- a/content/doc/addons/mongodb.md +++ b/content/doc/addons/mongodb.md @@ -82,20 +82,20 @@ Note that these features are available for all our databases add-ons, in additio ## 🔑 Rights and permissions -Add-ons are managed services, meaning that users have **standard access** to the database (role **owner**). Some operations like databases and users creation, as well as some settings modifications aren't available by default. This ensures optimal performances and security for managed services as configured by Clever Cloud. +Clever Cloud configures and maintains the MongoDB server. You have **standard access** to the database through the **owner** role, but some administrative operations and server settings aren't available directly. This ensures optimal performance and security. Authorized actions: + - Manage collections (create, delete…). - Manage indexes. - Manage documents. -If you think your system might require more advanced administrative access, [contact Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to explain your use case, and we will work with you to find a solution. +The following actions aren't available directly: -Here is the list of actions that you won't be able to perform: - Database administration (for example you won't be able to create new databases). -- Users administration (you won't be able to create other users than the one handled with our control plane, ie the base owner and read-only users). +- Users administration (you won't be able to create other users than the one handled with our control plane, i.e. the base owner and read-only users). - Server configuration update. - Cluster creation. - Backup frequency or retention control. -Ask Clever Cloud support if you want to perform one of these actions. +If your use case requires specific server parameters or one of these restricted operations, contact [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to discuss feasibility. diff --git a/content/doc/addons/mysql.md b/content/doc/addons/mysql.md index d678df651..fbfbe619b 100644 --- a/content/doc/addons/mysql.md +++ b/content/doc/addons/mysql.md @@ -70,21 +70,21 @@ As Shared databases (DEV) are shared between multiple applications and delays co ## 🔑 Rights and permissions -Add-ons are managed services, meaning that users have **standard access** to the database (**ALL privileges**). Some operations like databases and users creation, as well as some settings modifications aren't available by default. This ensures optimal performances and security for managed services as configured by Clever Cloud. +Clever Cloud configures and maintains the MySQL server. You have **standard access** to the database with **ALL privileges**, but some administrative operations and server settings aren't available directly. This ensures optimal performance and security. Authorized actions: + - Manage tables (create, delete…). - Manage indexes. -If you think your system might require more advanced administrative access, contact [Clever Cloud Support](https://console.clever-cloud.com/ticket-center-choice) to explain your use case, and we will work with you to find a solution. +The following actions aren't available directly: -Here is the list of actions that you won't be able to perform: - Database administration (for example you won't be able to create new databases). - Users administration (you won't be able to create other users than the one handled with our control plane, i.e. the base owner and read-only users). - Server configuration update. - Plugins installation. - Replica creation. - Backup frequency or retention control. -- Create Trigger or Function (Only on DEV plan) +- Create triggers or functions (DEV plans only). -Ask Clever Cloud support if you want to perform one of these actions. +If your use case requires specific server parameters or one of these restricted operations, contact [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to discuss feasibility. diff --git a/content/doc/addons/postgresql.md b/content/doc/addons/postgresql.md index 49c7d795f..7407861b8 100644 --- a/content/doc/addons/postgresql.md +++ b/content/doc/addons/postgresql.md @@ -170,23 +170,23 @@ If you want to use [pg_activity](https://github.com/dalibo/pg_activity) on a Pos ## 🔑 Rights and permissions -Add-ons are managed services, meaning that users have **standard access** to the database (role **owner**). Some operations like databases and users creation, as well as some settings modifications aren't available by default. This ensures optimal performances and security for managed services as configured by Clever Cloud. +Clever Cloud configures and maintains the PostgreSQL server. You have **standard access** to the database through the **owner** role, but some administrative operations and server settings aren't available directly. This ensures optimal performance and security. Authorized actions: + - Manage tables (create, delete…). - Manage schemas. - Manage indexes. - Access information from **pg_catalog** (except **pg_database** on DEV plan). - Access to basic maintenance operations such as *VACUUM* and *ANALYZE*. -If you think your system might require more advanced administrative access, [contact Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to explain your use case, and we will work with you to find a solution. +The following actions aren't available directly: -Here is the list of actions that you won't be able to perform: - Database administration (for example you won't be able to create new databases). - Users administration (you won't be able to create other users than the one handled with our control plane, i.e. the base owner and read-only users). - Server configuration update. - Extensions installation. - Replica creation. -- Back-up frequency or retention control. +- Backup frequency or retention control. -Ask Clever Cloud support if you want to perform one of these actions. +If your use case requires specific server parameters or one of these restricted operations, contact [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to discuss feasibility. diff --git a/content/doc/addons/redis.md b/content/doc/addons/redis.md index f90fa7f0a..877249b03 100644 --- a/content/doc/addons/redis.md +++ b/content/doc/addons/redis.md @@ -59,18 +59,18 @@ please contact the support to change its policy. ## 🔑 Rights and permissions -Add-ons are managed services, meaning that users have **controlled access** to the server. They are granted access to all proposed operations except changing the server configuration. Based on the plan, they are granted access to a fix amount of databases. This ensures optimal performances and security for managed services as configured by Clever Cloud. +Clever Cloud configures and maintains the Redis server. You have **controlled access** to its operations, except those that change the server configuration. Your plan determines the number of databases you can access. This ensures optimal performance and security. Authorized actions: + - Access to one or more databases depending on your plan. - Access to all Redis operations except *CONFIG* and *CLUSTER*. -- Set up replica via clever cloud console. +- Set up a replica from the Clever Cloud Console. -If you think your system might require more advanced administrative access, [contact Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to explain your use case, and we will work with you to find a solution. +The following actions aren't available directly: -Here is the list of actions that you won't be able to perform: - Server configuration update. - Modules installation. - Backup frequency or retention control. -Ask Clever Cloud support if you want to perform one of these actions. +If your use case requires specific server parameters or one of these restricted operations, contact [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) to discuss feasibility. From 3d748f65defc86b23a1192eca935c2bf2f9f1e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Brunat?= Date: Tue, 12 May 2026 15:19:33 +0200 Subject: [PATCH 100/214] administrate(network): remove outdated Unique IP pricing --- content/doc/administrate/network.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/doc/administrate/network.md b/content/doc/administrate/network.md index 0966c52e6..2f6152902 100644 --- a/content/doc/administrate/network.md +++ b/content/doc/administrate/network.md @@ -27,7 +27,7 @@ This service allows your queries to some external services to come from a fixed This service does not appear in the Console at the moment. The best is to ask the support team that will set it up for you and provide you with the needed information. -At the time of writing this doc, this service was billed 30€/month. +For pricing details, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). The price does not change with the number of applications that will use it. The IP depends on the zone, so ask the support about it. From e2c9458ec1a5f19c76f379f923b36ba68525fd58 Mon Sep 17 00:00:00 2001 From: Julie POUNY Date: Tue, 19 May 2026 11:32:24 +0200 Subject: [PATCH 101/214] develop(build cache): warn against disabling uploads on nano and pico --- content/doc/administrate/apps-management.md | 5 ++--- content/doc/develop/build-hooks.md | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/content/doc/administrate/apps-management.md b/content/doc/administrate/apps-management.md index ebfc53a04..1ffd6fd54 100644 --- a/content/doc/administrate/apps-management.md +++ b/content/doc/administrate/apps-management.md @@ -28,9 +28,8 @@ Stop functionality is useful during the development of the application to limit ![Manage your application from the Console](/images/app-management.png) -{{< callout type="info" >}} - If you set [`CC_DISABLE_BUILD_CACHE_UPLOAD`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables) environment variable to `true`, the cache archive won't be created nor uploaded. -{{< /callout >}} +> [!NOTE] +> If you set [`CC_DISABLE_BUILD_CACHE_UPLOAD`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables) environment variable to `true`, the cache archive won't be created nor uploaded. Don't use it with `nano` and `pico` plans which always use a build instance to create the build cache. ## Deploy an old commit diff --git a/content/doc/develop/build-hooks.md b/content/doc/develop/build-hooks.md index 2390cde7e..099613657 100644 --- a/content/doc/develop/build-hooks.md +++ b/content/doc/develop/build-hooks.md @@ -97,9 +97,8 @@ This hook is perfect for: - extra build steps that you want to cache (eg bundling your frontend assets) -{{< callout type="info" >}} - If you set [\`CC_DISABLE_BUILD_CACHE_UPLOAD\`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables) environment variable to `true`, the cache archive won't be created nor uploaded. -{{< /callout >}} +> [!NOTE] +> If you set [`CC_DISABLE_BUILD_CACHE_UPLOAD`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables) environment variable to `true`, the cache archive won't be created nor uploaded. Don't use it with `nano` and `pico` plans which always use a build instance to create the build cache. ### Pre Run From 8e3efff0b637e9ed3c0cca73d761ce77f398a1a5 Mon Sep 17 00:00:00 2001 From: Rachel Nascimento Date: Wed, 6 May 2026 18:07:38 +0200 Subject: [PATCH 102/214] administrate(network): document dedicated load balancers --- content/doc/administrate/network.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/content/doc/administrate/network.md b/content/doc/administrate/network.md index 2f6152902..50590f26c 100644 --- a/content/doc/administrate/network.md +++ b/content/doc/administrate/network.md @@ -47,6 +47,24 @@ We provide three kinds of VPN technologies: If you are interested, please ask the support / your sales contact for a quote. +### Dedicated load balancers + +By default, incoming traffic to your applications goes through Clever Cloud's shared load balancers, powered by [Sōzu](https://www.sozu.io/). For workloads that need isolated capacity, fixed inbound IP addresses or additional redundancy, you can request **dedicated load balancers**. + +Dedicated load balancers are especially relevant if you need to: + +- **Isolate your traffic** from other customers +- **Use fixed inbound IP addresses** for allowlists or compliance requirements +- **Handle high traffic volumes** without sharing load balancer capacity with other organisations +- **Add redundancy** to the network entry point of critical architectures + +Two configurations are available: + +- **Single load balancer**: one load balancer dedicated to your organisation. +- **High availability**: two dedicated load balancers make the network entry layer redundant, so it doesn't depend on a single load balancer. This configuration is recommended for critical architectures. + +This is a custom, quote-based option. To discuss your requirements and pricing, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) with your use case and target region. + ## The "Paris" region The Paris region is owned and handled by Clever Cloud. We own or entrust the associated AS's and From 5053c44647ec76d9c6ea687acc5158d0c362bd94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Brunat?= Date: Tue, 12 May 2026 15:25:26 +0200 Subject: [PATCH 103/214] addons(mysql): document replication --- content/doc/addons/mysql.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/content/doc/addons/mysql.md b/content/doc/addons/mysql.md index fbfbe619b..ce765a61c 100644 --- a/content/doc/addons/mysql.md +++ b/content/doc/addons/mysql.md @@ -42,6 +42,15 @@ If you need to import a very large dump, contact [Clever Cloud Support](https:// {{% content "db-migration" %}} +## Replication + +You can add up to two replicas to an existing MySQL database on Clever Cloud to enhance performance and reliability. Replication is available for MySQL 5.7 and later versions. Read-only replicas use [logical replication based on the binary log](https://dev.mysql.com/doc/refman/8.4/en/binlog-replication-configuration-overview.html) and can be deployed in a different availability zone (AZ) or region on request. + +If a primary server isn't available, a replica can be promoted as a standalone server and linked to applications. + +> [!NOTE] +> Replica creation and promotion aren't yet available through the API or the Console. To create or configure replicas, or to promote one, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). + ## Direct access {{< callout type="warning">}} From 300c8de92a38245136f437deda8ac225e8d81228 Mon Sep 17 00:00:00 2001 From: Pierre Zemb Date: Mon, 30 Mar 2026 10:03:14 +0200 Subject: [PATCH 104/214] addons(kv): add distributed behaviour and usage guide Co-Authored-By: Claude Opus 4.6 (1M context) --- content/doc/addons/materia-kv.md | 96 ++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 6 deletions(-) diff --git a/content/doc/addons/materia-kv.md b/content/doc/addons/materia-kv.md index 251efdc56..82a523fee 100644 --- a/content/doc/addons/materia-kv.md +++ b/content/doc/addons/materia-kv.md @@ -11,6 +11,9 @@ keywords: - distributed storage - nosql database - high availability +- transaction conflicts +- hot-spotting +- foundationdb draft: false aliases: - /doc/addons/materia-db-kv/ @@ -77,12 +80,6 @@ redis-cli -h $KV_HOST -p $KV_PORT --tls You can also deploy Materia KV add-ons with [Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs/resources/materiadb_kv) (OpenTofu compatible). -{{< callout type="info" >}} - -**Materia KV is in Beta testing phase** Each add-on is limited to 128 MB of storage, requests sent to the server can't exceed 5 MB. - -{{< /callout >}} - {{% content "kv-explorer" %}} ## Using the Redis API compatible layer @@ -410,3 +407,90 @@ We've prepared a few examples to help you get started with Materia KV: * [Materia KV raw TCP Ruby demo](https://github.com/CleverCloud/mkv-raw-tcp-ruby) * [Materia KV PHP sessions with TTL demo](https://github.com/CleverCloud/php-sessions-kv-example) * [Materia KV write via Redis API, read via GraphQL](https://github.com/CleverCloud/kv-graphql-example) + +## How Materia KV differs from Redis + +Materia KV supports many Redis commands, but runs them on a distributed FoundationDB cluster instead of Redis's single-threaded engine. This changes how concurrent writes, key expiration and size limits behave. + +### Concurrent execution instead of a single thread + +FoundationDB uses a concurrency model called **optimistic concurrency control** (OCC). Every Redis command you send to Materia KV executes inside its own FoundationDB transaction. When a command runs, it starts a transaction that reads from a consistent snapshot of the database and buffers all writes locally. At commit time, the system checks whether another transaction has modified any key that the current transaction read since the snapshot. If so, the transaction aborts and retries automatically. If not, the writes apply atomically. + +This means multiple clients can operate on the database in parallel without locks. Most of the time, transactions commit on the first attempt and latency stays low. Contention only arises when two transactions touch overlapping keys within a short time window—and when it does, the system resolves it through retries rather than blocking. + +Because each command is its own transaction, two clients issuing `INCR counter` at the same time create two independent transactions that both read and write the same key—and one of them conflicts. This is the fundamental difference from Redis, where commands are queued and executed one at a time. + +A useful mental model: **your reads determine whether you can conflict, and your writes determine what other transactions conflict with.** A transaction that only reads never conflicts. A transaction that only writes (without reading first) never conflicts either. Conflicts arise specifically from read-then-write patterns on the same key range, which is exactly what commands like `INCR`, `HSET`, and `SADD` do internally. + +### Conflicts and automatic retries + +When two concurrent transactions read and write overlapping keys, FoundationDB detects a **conflict** at commit time. The system retries the losing transaction automatically—your Redis client receives a normal response if a retry succeeds, but the operation takes longer because it runs more than once behind the scenes. + +A transaction can't commit more than **5 seconds** after its first read. If a transaction can't commit within this window due to repeated conflicts or a long-running operation, it fails and returns an error to the client. Normal-priority operations retry up to **5 times** with a maximum delay of **500 ms** between attempts. If all retries are exhausted, the client receives an error. + +Under heavy contention, this retry mechanism causes **tail latency spikes**. Materia KV can serve concurrent requests across the cluster, but individual requests may occasionally take tens of milliseconds when retries occur. Monitoring your p99 latency is a good way to detect emerging contention in your workload. + +### Hot-spotting: the main pitfall + +A **hot spot** occurs when many concurrent operations target the same key or a narrow range of keys. Because all those transactions read and write overlapping data, they serialise through repeated conflicts and retries—effectively reducing throughput to sequential execution, but with the added overhead of each failed attempt. + +In Redis, hot keys constrain the throughput of its single execution thread. In Materia KV, a hot key can also trigger retries and increase latency for operations that touch it. + +#### Counters: `INCR` and `DECR` + +The `INCR` command reads the current value, adds one, and writes the result. This is a textbook read-modify-write cycle. When many clients increment the same counter concurrently, every transaction reads the same value and attempts to write a new one. Only one succeeds per commit round; the rest retry. Under high concurrency, most attempts fail on each round, and throughput drops significantly. + +If you need a high-throughput counter, consider **sharding** it across multiple keys. For example, maintain `counter:{0}` through `counter:{N}` and have each client pick a shard at random. To read the total, sum all shards. This trades read convenience for write scalability—a standard distributed systems technique known as **partition-local counters**. + +#### Concurrent writes to a Hash or Set + +When you call `HSET myhash field1 value1` and another client calls `HSET myhash field2 value2` at the same time, you might expect no conflict because the fields are different. However, Materia KV maintains **internal cardinality indexes** to support commands like `HLEN` and `SCARD`. Every `HSET` or `SADD` on the same key updates this shared counter, causing conflicts between concurrent writers even when they target different fields or members. + +This means a single Hash or Set key that receives rapid concurrent writes becomes a hot spot, even if each writer touches a distinct field. If write throughput to a collection matters more than having a single logical key, consider splitting the collection across multiple keys (for example, `users:a-m` and `users:n-z`). + +#### Large batch operations + +Commands like `MSET` with many keys or `DEL` with many keys execute within a single FoundationDB transaction. The wider the key range touched, the larger the **conflict surface**—the set of keys that can cause other concurrent transactions to fail. Additionally, `DEL` caps at **100 keys per call**, and `MSET` with thousands of keys may exceed the **10 MB transaction size limit**. + +Break large batch operations into smaller chunks when working with many keys. This reduces the conflict window and keeps each transaction within size limits. + +#### Time to Live (TTL) refresh storms + +Web frameworks commonly call `EXPIRE` on a session key for every HTTP request to keep the session alive. When a user has multiple browser tabs open, each tab generates its own requests. The number of concurrent `EXPIRE` calls on the same session key multiplies with every open tab, and each `EXPIRE` is a write transaction. Concurrent calls conflict and retry, and under sustained load the retry budget runs out and the client receives errors. + +Before calling `EXPIRE`, call `TTL` or `PTTL` to read the remaining TTL. Read-only operations don't conflict. Only issue the `EXPIRE` if the TTL has dropped by a meaningful threshold, such as 60 seconds. This reduces redundant writes and the risk of conflicts. + +#### General guidance + +The common thread across all these scenarios is **write concentration**. Distribute your writes across the keyspace rather than funnelling them through a single key or a narrow prefix. When multiple clients need to update related data, partition the work so that each transaction touches a different subset of keys. If a write might be redundant—refreshing a TTL that has barely changed, or setting a value identical to the current one—guard it with a read first. Reads never cause conflicts and cost far less in a distributed transaction engine than unnecessary writes that trigger retries. + +### Key expiration on access only + +Redis expires keys using two mechanisms: an active background process that periodically samples keys with a TTL and deletes expired ones, and a lazy check that removes expired keys when they're accessed. Materia KV uses **lazy deletion only**. An expired key leaves the database when a client attempts to read or write it—not before. + +This has a few practical consequences. Expired keys continue to consume storage until they're accessed. The `DBSIZE` command may report a count that includes expired-but-not-yet-deleted keys. If your workload creates many short-lived keys that are never read again, those keys accumulate until accessed or until the database is flushed. + +The TTL itself is stored as an absolute Unix timestamp in milliseconds. The server clock is authoritative—clock differences between your application and Materia KV don't affect correctness. + +Active background cleanup of expired keys is under active development. Future releases include a background process that reclaims storage from expired keys without waiting for client access. + +### Stricter size limits + +Materia KV enforces stricter size limits than Redis due to the constraints of the underlying distributed transaction engine. + +| Limit | Redis | Materia KV | +|-------|-------|------------| +| Max key size | 512 MB | 8 KB | +| Max value size | 512 MB | 5 MB | +| Max transaction size | N/A | 10 MB (reads + writes) | +| Transaction duration | N/A | 5 seconds | +| Max keys per `DEL` | No limit | 100 | +| Storage per add-on | Plan-dependent | 128 MB (during Beta, can be increased) | + +The system enforces the 8 KB key limit and 5 MB value limit at the application level. The 10 MB transaction limit and 5-second duration are FoundationDB constraints that apply to the sum of all data read and written within a single command's transaction. + +### Stronger durability by default + +Materia KV keeps each command atomic through a serialisable FoundationDB transaction. Successful writes are synchronously replicated across three data centres in Paris, reducing exposure to a single-site failure. + +Redis durability depends on its persistence and replication configuration. Materia KV doesn't expose `fsync`, `appendonly` or replication settings: durability is part of the managed service. The trade-off is the conflict and retry mechanism described above. Distributed coordination under contention costs latency. From 86d5b7fa0b0a2bf97e8568009195d03a6a267d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Brunat?= Date: Wed, 19 Aug 2026 19:56:24 +0200 Subject: [PATCH 105/214] develop(request-flow): document OAuth2 Proxy configuration Co-authored-by: David Legrand --- content/doc/applications/python/uv.md | 7 +- content/doc/develop/_index.md | 9 +- content/doc/develop/oauth2-proxy.md | 156 ++++++++++++++++++ content/doc/develop/request-flow.md | 46 +++--- .../reference-environment-variables.md | 2 + shared/request-flow.md | 10 +- 6 files changed, 200 insertions(+), 30 deletions(-) create mode 100644 content/doc/develop/oauth2-proxy.md diff --git a/content/doc/applications/python/uv.md b/content/doc/applications/python/uv.md index 51fe77282..2933ad892 100644 --- a/content/doc/applications/python/uv.md +++ b/content/doc/applications/python/uv.md @@ -40,22 +40,23 @@ The uv cache (`~/.cache/uv`) is included in the build cache to speed up subseque ## Run phase -The application starts with the command defined in `CC_PYTHON_UV_RUN_COMMAND`. It must start an HTTP server listening on `0.0.0.0:8080`. +The application starts with the command defined in `CC_PYTHON_UV_RUN_COMMAND`. It must start an HTTP server listening on `0.0.0.0:$PORT`. Clever Cloud sets `PORT` to `8080` by default. Set it to `9000` when you enable at least one [Request Flow middleware](/doc/develop/request-flow/#port-management). `CC_RUN_COMMAND` takes precedence over `CC_PYTHON_UV_RUN_COMMAND` if both are set. | Name | Description | Required | Default | -|------|-------------|----------|---------| +| ---- | ----------- | -------- | ------- | | `CC_PYTHON_UV_RUN_COMMAND` | Command to start the application (e.g. `uv run python app.py`) | Yes | - | | `CC_RUN_COMMAND` | Overrides `CC_PYTHON_UV_RUN_COMMAND` if set | No | - | | `ENVIRONMENT` | Set to `development` to include dev dependencies during build | No | `production` | +| `PORT` | Port used by your HTTP server | No | `8080` | ## Differences with legacy Python deployment With native uv deployment: - No Nginx, uWSGI, or Gunicorn is involved -- Your application listens on port `8080` (not `9000`) +- Your application manages its HTTP server and listens on the port defined by `PORT` - `CC_PYTHON_MODULE` and `CC_PYTHON_BACKEND` are ignored - [Redirection.io, Varnish and custom proxies](/doc/develop/request-flow/) are configured through Request Flow, not Nginx - [Server configuration](/doc/applications/python/servers/) settings do not apply diff --git a/content/doc/develop/_index.md b/content/doc/develop/_index.md index 1bb08f165..c59fd4718 100644 --- a/content/doc/develop/_index.md +++ b/content/doc/develop/_index.md @@ -16,13 +16,14 @@ aliases: --- {{< cards >}} + {{< card link="/developers/doc/develop/tasks" title="Clever Tasks" icon="play-circle" >}} + {{< card link="/developers/doc/develop/healthcheck" title="Deployment healthcheck path" icon="check" >}} {{< card link="/developers/doc/develop/build-hooks" title="Deployment hooks" icon="rocket-launch" >}} {{< card link="/developers/doc/reference/reference-environment-variables" title="Environment variables reference" icon="creds" >}} {{< card link="/developers/doc/develop/env-variables" title="How environment variables work" icon="question-mark-circle" >}} - {{< card link="/developers/doc/develop/workers" title="Workers" icon="arrow-path" >}} - {{< card link="/developers/doc/develop/tasks" title="Clever Tasks" icon="play-circle" >}} - {{< card link="/developers/doc/develop/healthcheck" title="Deployment healthcheck path" icon="check" >}} + {{< card link="/developers/doc/develop/network-groups" title="Network Groups" icon="tcp-ip-service" >}} + {{< card link="/developers/doc/develop/oauth2-proxy" title="OAuth2 Proxy" icon="lock-closed" >}} {{< card link="/developers/doc/develop/request-flow" title="Request Flow" icon="traffic-light" >}} {{< card link="/developers/doc/develop/varnish" title="Varnish as HTTP cache" icon="arrow-trending-up" >}} - {{< card link="/developers/doc/develop/network-groups" title="Network Groups" icon="tcp-ip-service" >}} + {{< card link="/developers/doc/develop/workers" title="Workers" icon="arrow-path" >}} {{< /cards >}} diff --git a/content/doc/develop/oauth2-proxy.md b/content/doc/develop/oauth2-proxy.md new file mode 100644 index 000000000..a7f29c82f --- /dev/null +++ b/content/doc/develop/oauth2-proxy.md @@ -0,0 +1,156 @@ +--- +type: docs +linkTitle: OAuth2 Proxy +title: OAuth2 Proxy +description: Add OAuth2 Proxy authentication to applications through Request Flow on Clever Cloud without changing application code +keywords: +- oauth2-proxy +- authentication +- oidc +- sso +- request flow +- reverse proxy +--- + +## Overview + +[Request Flow](/doc/develop/request-flow/) puts authentication in front of any application through [OAuth2 Proxy](https://oauth2-proxy.github.io/oauth2-proxy/), with no change to your code: a few environment variables are enough. Clever Cloud starts the middleware, allocates its port, points it at your application, and keeps it in the chain alongside Varnish, Redirection.io or a proxy of your own with the same flexibility. It's available on every runtime that supports Request Flow. + +OAuth2 Proxy redirects unauthenticated visitors to the identity provider you configure, and only authenticated requests reach your application. This suits a staging environment, an internal dashboard or an administration interface. + +Clever Cloud sets the port OAuth2 Proxy listens on and the address of your application. Every other setting comes from `OAUTH2_PROXY_*` environment variables, which map one to one to the [OAuth2 Proxy configuration options](https://oauth2-proxy.github.io/oauth2-proxy/configuration/overview). + +## Enable OAuth2 Proxy + +Add `oauth2-proxy` to the `CC_REQUEST_FLOW` environment variable: + +```bash +CC_REQUEST_FLOW="oauth2-proxy" +``` + +Request Flow allows you to use OAuth2 Proxy with other middleware such as [Varnish](/doc/develop/varnish/) or [Redirection.io](https://redirection.io/) for example. List every middleware in the order you want, from the public port to your application: + +```bash +CC_REQUEST_FLOW="oauth2-proxy,varnish" +``` + +## Minimal configuration + +OAuth2 Proxy validates its configuration at startup and exits when a required setting is missing, which fails your deployment. The following variables are the smallest working set for a GitHub identity provider, and the same structure applies to every provider. Replace every value with your own: + +```bash +CC_REQUEST_FLOW="oauth2-proxy" +OAUTH2_PROXY_PROVIDER="github" +OAUTH2_PROXY_CLIENT_ID="" +OAUTH2_PROXY_CLIENT_SECRET="" +OAUTH2_PROXY_COOKIE_SECRET="" +OAUTH2_PROXY_EMAIL_DOMAINS="example.com" +OAUTH2_PROXY_REDIRECT_URL="https://app.example.com/oauth2/callback" +``` + +The redirect URL must point to the public domain of your application, followed by `/oauth2/callback`. Declare that exact URL in your identity provider as an authorised callback, otherwise the provider rejects the login with a `redirect_uri mismatch` error. + +For an OpenID Connect provider such as Okta or Microsoft Entra ID, set the provider to `oidc` and give the issuer URL. The `email` scope feeds the address that authorization rules check, so request it explicitly: + +```bash +OAUTH2_PROXY_PROVIDER="oidc" +OAUTH2_PROXY_OIDC_ISSUER_URL="https://sso.example.com" +OAUTH2_PROXY_SCOPE="openid email profile" +``` + +Keycloak has a provider of its own, `keycloak-oidc`, pointing at the realm you want: + +```bash +OAUTH2_PROXY_PROVIDER="keycloak-oidc" +OAUTH2_PROXY_OIDC_ISSUER_URL="https://sso.example.com/realms/internal" +OAUTH2_PROXY_SCOPE="openid email profile" +``` + +> [!NOTE] +> The [OAuth2 Proxy client installation provider](https://github.com/please-openit/keycloak-oauth2proxy-client-installation-provider) writes these variables for you from your Keycloak client settings. Install the extension in the `providers` folder of your [Keycloak add-on](/doc/addons/keycloak/#custom-themes-and-plugins), then download "Oauth2-proxy environment variables" from the Installation tab of your client. [This video](https://www.youtube.com/watch?v=Jo-Njxsxq-8) presents it, in French. + +## Generate the cookie secret + +OAuth2 Proxy encrypts session cookies with an AES cipher and accepts a secret of 16, 24, or 32 bytes only. A hexadecimal string generated with `openssl rand -hex 32` counts as 64 bytes and gets rejected at startup. Generate a valid secret with: + +```bash +openssl rand -base64 32 | tr '+/' '-_' +``` + +Use a different secret for each application. Changing the secret invalidates every existing session and signs all users out. + +## Authorize users + +Authenticating a visitor and authorizing them are two different steps. Once the identity provider confirms who the visitor is, OAuth2 Proxy compares the email address it received against your authorization rules, and answers `403 Forbidden` when no rule matches, even though the login succeeded. + +One of these rules is mandatory at startup, which prevents an accidentally open proxy. Set `OAUTH2_PROXY_EMAIL_DOMAINS` to a comma-separated list of domains, prefixing a domain with a dot to include its subdomains, or set `OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE` to the path of a file listing one authorized address per line: + +```bash +OAUTH2_PROXY_EMAIL_DOMAINS="example.com,.corp.example.com" +``` + +The value `*` authorizes any address the provider validates, which means you rely entirely on the provider for identity checks. With an identity provider restricted to your organisation, this is the expected setting. With a public provider such as GitHub or Google, it accepts every account of that provider, so pick the scope you want: a domain list, an authenticated emails file, or a provider-level rule such as `OAUTH2_PROXY_GITHUB_ORG`. + +## Pass the identity to your application + +OAuth2 Proxy forwards the visitor identity to your application by default through `X-Forwarded-User`, `X-Forwarded-Groups`, `X-Forwarded-Email` and `X-Forwarded-Preferred-Username` headers. + +To also forward the OpenID Connect ID token in the `Authorization` header, enable: + +```bash +OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER="true" +``` + +This suits an application that validates the token itself. Only trust these identity headers on requests that have passed through OAuth2 Proxy. + +## Listen on the right port + +When your application manages its own HTTP server, it must listen on port `9000` while Request Flow is active. Clever Cloud handles the backend configuration for runtimes with a managed web server or port. In every other runtime, set the port yourself: + +```bash +PORT="9000" +``` + +> [!NOTE] +> This works as long as your application listens on `0.0.0.0:$PORT` rather than on a hardcoded port. Read the [Request Flow port management](/doc/develop/request-flow/#port-management) section for the details of the whole chain. + +## Keep the health check working + +The platform health check requests your application through the public port, which OAuth2 Proxy now answers. A redirect to the login page still counts as a healthy answer, so the default health check keeps working. + +This changes as soon as you configure [`CC_HEALTH_CHECK_PATH`](/doc/develop/healthcheck/), which expects a `2xx` status. An authenticated path never returns one to the health check, so exclude it from authentication: + +```bash +CC_HEALTH_CHECK_PATH="/health" +OAUTH2_PROXY_SKIP_AUTH_ROUTES="^/health$" +``` + +This exclusion makes the health check path publicly accessible. Limit its response to the application's health status and don't expose sensitive data. + +## Scale to several instances + +OAuth2 Proxy stores sessions in the cookie by default, so every instance of your application validates them without shared state. Horizontal scaling and instance replacement need no extra configuration. + +Tokens with many claims, particularly Microsoft Entra ID tokens carrying group memberships, can make sessions exceed the 4 kB limit for a single cookie. OAuth2 Proxy splits large sessions across several cookies, but the resulting headers can still exceed browser or proxy limits. In that case, store sessions in a [Redis add-on](/doc/addons/redis/) and copy its connection URL into the variable: + +```bash +OAUTH2_PROXY_SESSION_STORE_TYPE="redis" +OAUTH2_PROXY_REDIS_CONNECTION_URL="redis://:password@host:port" +``` + +## Troubleshooting + +OAuth2 Proxy logs its configuration errors and exits, so read the deployment logs from the bottom up. The health check reports the public port as closed, which is a consequence of the middleware being down rather than a problem with your application: + +| Log message | Cause | +| ----------- | ----- | +| `cookie_secret must be 16, 24, or 32 bytes` | The secret has the wrong length, see [Generate the cookie secret](#generate-the-cookie-secret) | +| `missing setting for email validation` | The configuration carries neither `OAUTH2_PROXY_EMAIL_DOMAINS` nor `OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE` | +| `Your application is not listening on 8080` | OAuth2 Proxy failed to start, the preceding message holds the reason | +| `Some software are not listening as expected: 9000` | OAuth2 Proxy runs, your application doesn't listen on port `9000` | + +A login that loops between your application and the identity provider points to `OAUTH2_PROXY_REDIRECT_URL` not matching the callback declared in the provider. A `403 Forbidden` after a successful login points to an email address outside your authorization rules, or to a missing `email` scope. + +- [Learn more about Request Flow](/doc/develop/request-flow/) +- [Learn more about OAuth2 Proxy configuration](https://oauth2-proxy.github.io/oauth2-proxy/) +- [Configure your health check](/doc/develop/healthcheck/) diff --git a/content/doc/develop/request-flow.md b/content/doc/develop/request-flow.md index fe63d3464..34ef2f009 100644 --- a/content/doc/develop/request-flow.md +++ b/content/doc/develop/request-flow.md @@ -23,10 +23,10 @@ Request Flow is Clever Cloud's automatic middleware chaining mechanism. It confi ## Supported services | Service | Activation | Description | -|---------|-----------|-------------| +| ------- | ---------- | ----------- | | `block` | `CC_REQUEST_FLOW="block"` | Blocks public access with a `200 OK` response. Other ports remain accessible through [Network Groups](/doc/develop/network-groups/) | -| `custom` | `CC_REQUEST_FLOW_CUSTOM` | Any custom reverse proxy | -| `oauth2-proxy` | `CC_REQUEST_FLOW="oauth2-proxy"` | Authentication proxy using [OAuth2 Proxy](https://oauth2-proxy.github.io/oauth2-proxy/) | +| `custom` | `CC_REQUEST_FLOW="custom"` | Any custom reverse proxy, started with `CC_REQUEST_FLOW_CUSTOM` | +| `oauth2-proxy` | `CC_REQUEST_FLOW="oauth2-proxy"` | Authentication proxy using [OAuth2 Proxy](/doc/develop/oauth2-proxy/) | | `otoroshi-challenge` | `OTOROSHI_CHALLENGE_SECRET` | [Otoroshi](/doc/addons/otoroshi/) challenge verification proxy | | `redirectionio` | `CC_REDIRECTIONIO_PROJECT_KEY` | HTTP redirects, rewrites, SEO | | `varnish` | `clevercloud/varnish.vcl` file or `CC_VARNISH_FILE` | HTTP cache accelerator | @@ -39,20 +39,26 @@ When no `CC_REQUEST_FLOW` is set, Clever Cloud detects and activates services au - If a `clevercloud/varnish.vcl` file exists (or `CC_VARNISH_FILE` is set), Varnish is activated - If `CC_REDIRECTIONIO_PROJECT_KEY` is set, Redirection.io is activated -All three can be active simultaneously. Default order: Otoroshi Challenge first, then Varnish, then Redirection.io. +When automatically detected, Otoroshi Challenge, Varnish and Redirection.io can run simultaneously, in this order: Otoroshi Challenge, Varnish, then Redirection.io. + +No automatic detection exists for `oauth2-proxy` and `custom`. Only `CC_REQUEST_FLOW` activates them. Setting this variable also replaces automatic detection for the whole chain, so a `CC_REQUEST_FLOW="oauth2-proxy"` on an application holding a `clevercloud/varnish.vcl` file starts OAuth2 Proxy alone. List every middleware you need: `CC_REQUEST_FLOW="oauth2-proxy,varnish"`. ## Port management -Request Flow allocates ports in a chain from port `8080` (public) down to the application: +Request Flow allocates middleware ports in a chain from port `8080` (public) down to your application. For runtimes where you configure the application HTTP server yourself: - With no middleware: your application listens directly on port `8080` - With one middleware: the middleware listens on `8080`, forwards to your application on port `9000` -- With two middleware: first listens on `8080`, forwards to second on `8081`, which forwards to the application on `9000` - -Your application must listen on port `8080` when no middleware is active, or on port `9000` when at least one middleware is configured. +- With two middleware services: the first listens on `8080`, forwards to the second on `8081`, which forwards to the application on `9000` > [!NOTE] -> In runtimes where Clever Cloud manages the port configuration (FrankenPHP, Java, PHP, Static), port allocation is handled transparently with no additional configuration. +> FrankenPHP, Java, PHP, legacy Python, Ruby and Static applications need no additional configuration, as Clever Cloud manages their web server or port transparently. Python applications using native uv support manage their own HTTP server and follow the port rule above. + +In every runtime where your application manages its own HTTP server, have it listen on `0.0.0.0:$PORT` and set `PORT` to `9000` when a middleware is active: + +```bash +PORT="9000" +``` ## Explicit configuration with CC_REQUEST_FLOW @@ -62,7 +68,7 @@ To control the order or selection of middleware, set `CC_REQUEST_FLOW` to a comm CC_REQUEST_FLOW="redirectionio,varnish" ``` -This inverts the default order: Redirection.io listens on `8080`, forwards to Varnish on `8081`, which forwards to the application on `9000`. +For an application that manages its own HTTP server, this inverts the default order: Redirection.io listens on `8080`, forwards to Varnish on `8081`, which forwards to the application on `9000`. ### Disable Request Flow @@ -84,12 +90,7 @@ CC_REQUEST_FLOW="block" ### Health check with block mode -By default, `block` responds `200 OK` regardless of your application's actual state. If [`CC_HEALTH_CHECK_PATH` or `CC_HEALTH_CHECK_PATH_0` to `CC_HEALTH_CHECK_PATH_5`](/doc/develop/healthcheck/) are configured, the blocking service also checks these paths on your application and responds accordingly: - -- `200 OK` if all configured paths return a `2xx` status -- `503 Service Unavailable` if the application is down or any path returns a non-`2xx` status - -This way, the platform's health check still reflects the actual state of your application even when public traffic is blocked. +With `block` enabled, the deployment health check only verifies that the blocking service listens on port `8080` and responds `200 OK`. It doesn't send an HTTP request to your application, including when you configure [`CC_HEALTH_CHECK_PATH` or `CC_HEALTH_CHECK_PATH_0` to `CC_HEALTH_CHECK_PATH_5`](/doc/develop/healthcheck/). ## Custom middleware @@ -100,7 +101,8 @@ CC_REQUEST_FLOW="redirectionio,custom,varnish" CC_REQUEST_FLOW_CUSTOM="./my-proxy --listen @@LISTEN_PORT@@ --forward @@FORWARD_PORT@@" ``` -In this example: +For an application that manages its own HTTP server, this example produces the following chain: + - Redirection.io listens on `8080`, forwards to custom middleware on `8081` - Custom middleware listens on `8081`, forwards to Varnish on `8082` - Varnish listens on `8082`, forwards to the application on `9000` @@ -108,15 +110,21 @@ In this example: ## Environment variables reference | Name | Description | -|------|-------------| +| ---- | ----------- | | `CC_REQUEST_FLOW` | Comma-separated list of middleware to chain (e.g. `varnish,redirectionio`). Special values: `disable`, `block` | | `CC_REQUEST_FLOW_CUSTOM` | Command to start a custom middleware. Must contain `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders | | `CC_REDIRECTIONIO_PROJECT_KEY` | Redirection.io project key. Activates Redirection.io in the request flow | | `CC_VARNISH_FILE` | Path to a custom Varnish VCL file (default: `clevercloud/varnish.vcl`) | | `OTOROSHI_CHALLENGE_SECRET` | Otoroshi challenge secret. Activates Otoroshi Challenge verification in the request flow | +## Troubleshooting + +A middleware that fails to start leaves the public port closed, and the deployment ends with `Your application is not listening on 8080`. This message names the public port of the chain, which belongs to the first middleware rather than to your application. Read the preceding deployment logs: the middleware logs its own error before exiting. + +The message `Some software are not listening as expected: 9000` means the opposite. Every middleware runs, and your application doesn't listen on the port the chain forwards to. Check the [port management](#port-management) section for the port your runtime expects. + - [Learn more about Varnish on Clever Cloud](/doc/develop/varnish/) - [Learn more about Redirection.io](https://redirection.io/) -- [Learn more about OAuth2 Proxy](https://oauth2-proxy.github.io/oauth2-proxy/) +- [Learn more about OAuth2 Proxy on Clever Cloud](/doc/develop/oauth2-proxy/) - [Learn more about Otoroshi on Clever Cloud](/doc/addons/otoroshi/) - [Learn more about Network Groups](/doc/develop/network-groups/) diff --git a/content/doc/reference/reference-environment-variables.md b/content/doc/reference/reference-environment-variables.md index 0cedc10b5..98ca417b8 100644 --- a/content/doc/reference/reference-environment-variables.md +++ b/content/doc/reference/reference-environment-variables.md @@ -111,6 +111,8 @@ Use these to define [commands to run](/doc/develop/build-hooks) between various |[`CC_METRICS_PROMETHEUS_PORT`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the port on which the Prometheus endpoint is available | 9100 | |[`CC_METRICS_PROMETHEUS_RESPONSE_TIMEOUT`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the timeout in seconds to collect the application metrics. This value **must** be below 60 seconds as data are collected every minutes | 3 | |[`CC_METRICS_PROMETHEUS_USER`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the user for the basic auth of the Prometheus endpoint | | +|[`CC_REQUEST_FLOW`](/doc/develop/request-flow "Request Flow") | Comma-separated list of middleware to chain between the public port and your application (`block`, `custom`, `oauth2-proxy`, `otoroshi-challenge`, `redirectionio`, `varnish`). Special values: `disable`, `block` | | +|[`CC_REQUEST_FLOW_CUSTOM`](/doc/develop/request-flow/#custom-middleware "Request Flow") | Command starting a custom middleware, with the `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders | | |[`CC_VARNISH_FILE`](/doc/develop/varnish "Cache") | The path to the Varnish configuration file, relative to your application root | `/clevercloud/varnish.vcl` | |[`CC_VARNISH_STORAGE_SIZE`](/doc/develop/varnish "Cache") | Configure the size of the Varnish cache. | 1G | |[`CC_WORKER_COMMAND`](/doc/develop/workers "Workers") | Command to run in background as a worker process. You can run multiple workers. | | diff --git a/shared/request-flow.md b/shared/request-flow.md index 1fe40c7f3..1aca34131 100644 --- a/shared/request-flow.md +++ b/shared/request-flow.md @@ -1,15 +1,17 @@ -## Request Flow: Varnish, Redirection.io, custom proxy +## Request Flow: Varnish, Redirection.io, OAuth2 Proxy, custom proxy -Request Flow automatically chains reverse proxies between port `8080` (public) and your application, managing port allocation with no manual configuration. Supported services are activated by their presence in your project: +Request Flow automatically chains reverse proxies between port `8080` (public) and your application, allocating middleware ports automatically. Some services are detected from your configuration, while others must be listed explicitly in `CC_REQUEST_FLOW`: - **Otoroshi Challenge**: set `OTOROSHI_CHALLENGE_SECRET` - **Varnish**: add a `clevercloud/varnish.vcl` file or set `CC_VARNISH_FILE` - **Redirection.io**: set `CC_REDIRECTIONIO_PROJECT_KEY` +- **OAuth2 Proxy**: set `CC_REQUEST_FLOW=oauth2-proxy` and its `OAUTH2_PROXY_*` settings -All three can be active simultaneously. To control the order, set `CC_REQUEST_FLOW` (e.g. `redirectionio,varnish`). To add a custom middleware, include `custom` in the chain and define `CC_REQUEST_FLOW_CUSTOM` with `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders. To block public access, set `CC_REQUEST_FLOW=block`. +Multiple services can run simultaneously. Setting `CC_REQUEST_FLOW` replaces automatic detection, so list every service you need in order (e.g. `oauth2-proxy,varnish`). To add a custom middleware, include `custom` in the chain and define `CC_REQUEST_FLOW_CUSTOM` with `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders. To block public access, set `CC_REQUEST_FLOW=block`. -When at least one middleware is active, your application must listen on port `9000` instead of `8080`. +If your application manages its own HTTP server, configure it to listen on port `9000` instead of `8080` when at least one middleware is active. Clever Cloud handles this automatically for runtimes with a managed web server. - [Learn more about Request Flow](/doc/develop/request-flow/) - [Learn more about Varnish on Clever Cloud](/doc/develop/varnish/) +- [Learn more about OAuth2 Proxy on Clever Cloud](/doc/develop/oauth2-proxy/) - [Learn more about Redirection.io](https://redirection.io/) From 2f4a4637093314bafbaa13c5b9bd71f7c5c600b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Brunat?= Date: Mon, 24 Aug 2026 14:59:33 +0200 Subject: [PATCH 106/214] chore(vale): update documentation vocabulary --- .github/styles/config/vocabularies/Doc/accept.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/styles/config/vocabularies/Doc/accept.txt b/.github/styles/config/vocabularies/Doc/accept.txt index 148795ae7..bbc33985b 100644 --- a/.github/styles/config/vocabularies/Doc/accept.txt +++ b/.github/styles/config/vocabularies/Doc/accept.txt @@ -32,6 +32,7 @@ DNS Dockerfile downtimes eg +Entra EOL ESLint failover @@ -45,6 +46,7 @@ Glassfish Gradle gradle Grafana +hardcoded healthcheck Heptapod Hextra @@ -65,6 +67,7 @@ Materia Matomo maven Metabase +middleware monolog monorepo monorepository @@ -76,6 +79,7 @@ nmap npm Nuxt OAuth +Okta Otoroshi packageManager Payara From a588b117ba70987ec5f9a31c5339095983f91437 Mon Sep 17 00:00:00 2001 From: bienfait-git Date: Wed, 1 Jul 2026 11:37:39 +0200 Subject: [PATCH 107/214] guides: add Hono Document how to scaffold and deploy a Hono application with Node.js, including platform port handling, graceful shutdown, build configuration and Clever Tools deployment. Co-Authored-By: Claude Sonnet 5 Co-Authored-By: David Legrand --- .../styles/config/vocabularies/Doc/accept.txt | 1 + content/guides/_index.md | 2 +- content/guides/hono.md | 129 ++++++++++++++++++ data/icons.yaml | 2 + 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 content/guides/hono.md diff --git a/.github/styles/config/vocabularies/Doc/accept.txt b/.github/styles/config/vocabularies/Doc/accept.txt index bbc33985b..865e5b2e0 100644 --- a/.github/styles/config/vocabularies/Doc/accept.txt +++ b/.github/styles/config/vocabularies/Doc/accept.txt @@ -50,6 +50,7 @@ hardcoded healthcheck Heptapod Hextra +Hono hostname Infinispan jarName diff --git a/content/guides/_index.md b/content/guides/_index.md index 47d986359..dfd115160 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -36,6 +36,7 @@ llmsTxtOptional: true {{< card link="/developers/guides/fluentd/" title="Fluentd" subtitle= "How to deploy Fluentd using Docker on Clever Cloud" icon="fluentd" >}} {{< card link="/developers/guides/go-echoip" title="Echo IP" subtitle= "How to deploy EchoIP based on a Go application on Clever Cloud" icon="go" >}} {{< card link="/developers/guides/hexo" title="Hexo" subtitle= "Build and deploy a static Hexo based website on Clever Cloud" icon="hexo" >}} + {{< card link="/developers/guides/hono" title="Hono" subtitle= "Deploy a Hono application with Node.js on Clever Cloud" icon="hono" >}} {{< card link="/developers/guides/hugo" title="Hugo" subtitle= "Build and deploy a static Hugo based website on Clever Cloud" icon="hugo" >}} {{< card link="/developers/guides/hugo-static-s3" title="Hugo + Cellar" subtitle= "Build and deploy a static Hugo based website and host it using Cellar" icon="hugo" >}} {{< card link="/developers/guides/kibana" title="Kibana" subtitle= "Deploy and customize a Kibana server on Clever Cloud" icon="kibana" >}} @@ -56,4 +57,3 @@ llmsTxtOptional: true {{< card link="/developers/guides/tutorial-symfony" title="Symfony" subtitle= "Deploy a Symfony application on Clever Cloud" icon="symfony" >}} {{< card link="/developers/guides/tutorial-wordpress" title="WordPress" subtitle= "Deploy WordPress on Clever Cloud" icon="wordpress" >}} {{< /cards >}} - diff --git a/content/guides/hono.md b/content/guides/hono.md new file mode 100644 index 000000000..dc0b3e5a6 --- /dev/null +++ b/content/guides/hono.md @@ -0,0 +1,129 @@ +--- +type: docs +linkTitle: Hono +title: Hono +description: Deploy a Hono application on Clever Cloud with the Node.js runtime and a step-by-step configuration guide +keywords: +- hono +- node.js +- javascript framework +- typescript +- api +- web framework +--- + +{{< hextra/hero-subtitle >}} + Deploy Hono, a lightweight web framework built on Web Standards, with the Node.js runtime on Clever Cloud. +{{< /hextra/hero-subtitle >}} + +Hono supports multiple JavaScript runtimes. This guide uses the [Clever Cloud Node.js runtime](/developers/doc/applications/nodejs/) with the [@hono/node-server adapter](https://hono.dev/docs/getting-started/nodejs). + +To follow this guide with a fresh project, scaffold a new Hono application using the official CLI (you'll need [Node.js](https://nodejs.org/en/download) and [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)). Pass `-t nodejs` to select the Node.js template directly. When prompted, confirm the dependency installation and select `npm` as package manager: + +```bash +npm create hono@latest myHonoApp -- -t nodejs +cd myHonoApp +git init +``` + +## Deploy Hono with Node.js + +To deploy your Hono project to Clever Cloud, adapt its entry point, then create and configure the application. + +### Adapt the entry point + +The `nodejs` template's `src/index.ts` starts a server on port 3000 and has no shutdown handling. Adapt both behaviours before deploying. + +Clever Cloud sets `PORT` environment variable to `8080` by default and expects the application to listen on every network interface. Read it rather than hardcoding this value to remain compatible with [Request Flow port allocation](/developers/doc/develop/request-flow/#port-management), keep port 3000 as a local fallback, and set the hostname to `0.0.0.0`. Also close the server on `SIGINT` and `SIGTERM` so in-flight requests can finish during a restart or zero-downtime deployment: + +```typescript +// src/index.ts +import { serve } from '@hono/node-server' +import { Hono } from 'hono' + +const app = new Hono() + +app.get('/', (c) => { + return c.text('Hello Hono!') +}) + +const server = serve({ + fetch: app.fetch, + hostname: '0.0.0.0', + port: Number(process.env.PORT) || 3000 +}, (info) => { + console.log(`Server is running on http://localhost:${info.port}`) +}) + +const shutdown = () => { + server.close((err) => { + if (err) { + console.error(err) + process.exit(1) + } + process.exit(0) + }) +} + +process.on('SIGINT', shutdown) +process.on('SIGTERM', shutdown) +``` + +The generated `package.json` defines three scripts: `dev` runs the server with live reload, `build` compiles TypeScript to `dist/` with `tsc`, and `start` runs the compiled output with `node dist/index.js`. + +### Create a Node.js application + +Install [Clever Tools](/developers/doc/cli/) and create a Node.js application linked to your project folder: + +```bash +npm i -g clever-tools +clever login + +clever create -t node + +# Or link an existing Clever Cloud application: +clever link your_app_name_or_ID +``` + +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + +### Configure the application + +```bash +clever env set CC_NODE_DEV_DEPENDENCIES install +clever env set CC_POST_BUILD_HOOK "npm run build" +``` + +By default, Clever Cloud only installs production dependencies, so `typescript` would be missing when `tsc` runs. `CC_NODE_DEV_DEPENDENCIES` includes development dependencies, and `CC_POST_BUILD_HOOK` builds `dist/` before the generated `start` script runs. No `PORT` variable needs to be set for this deployment because Clever Cloud injects it automatically. If your project grows, you can [increase the build or runtime instance size](/developers/doc/administrate/scalability/). + +### Deploy your code + +The Hono template does not ignore its generated `dist/` directory. Add it to `.gitignore`: + +```gitignore {filename=".gitignore"} +dist/ +``` + +Then commit and deploy the project: + +```bash +git add . +git commit -m "First deploy" + +clever deploy +clever open +``` + +You can display your application's URL or add a custom domain. A custom domain also requires [DNS configuration](/developers/doc/administrate/domain-names/): + +```bash +clever domain +clever domain add your.website.tld +``` + +## Learn more + +{{< cards >}} + {{< card link="/developers/doc/applications/nodejs" title="Deploy a Node.js application" subtitle="Learn more on deploying a Node.js application" icon="node" >}} + {{< card link="https://hono.dev/docs" title="Hono documentation" subtitle="Learn more about the Hono framework" icon="hono" >}} +{{< /cards >}} diff --git a/data/icons.yaml b/data/icons.yaml index 35ce9c087..022e58b60 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -111,6 +111,8 @@ play: fluentd: +hono: + # Static 11ty: astro: From 9b441c565a5ae1e5a4c84e91dab043693ddcc957 Mon Sep 17 00:00:00 2001 From: Robin Beaudru Date: Sun, 28 Jun 2026 10:53:31 +0200 Subject: [PATCH 108/214] guides: add SvelteKit Document static site generation, single-page application and server-side rendering deployments with the Static and Node.js runtimes. Also separate the Git and Clever Tools commands in the shared Static deployment instructions. Co-Authored-By: Claude Sonnet 5 Co-Authored-By: Claude Sonnet 4.6 Co-Authored-By: David Legrand --- content/guides/_index.md | 1 + content/guides/sveltekit.md | 232 ++++++++++++++++++++++++++++++++++++ data/icons.yaml | 1 + shared/static-deploy.md | 1 + 4 files changed, 235 insertions(+) create mode 100644 content/guides/sveltekit.md diff --git a/content/guides/_index.md b/content/guides/_index.md index dfd115160..a7dfd8604 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -54,6 +54,7 @@ llmsTxtOptional: true {{< card link="/developers/guides/ruby-on-rails" title="Ruby On Rails" subtitle= "How to deploy Ruby on Rails framework" icon="ruby" >}} {{< card link="/developers/guides/ruby-rack" title="Ruby Rack" subtitle= "Set up a Ruby Rack application and deploy on Clever Cloud" icon="ruby" >}} {{< card link="/developers/guides/ruby-rack-app-tutorial" title="Ruby Rack (full tutorial)" subtitle= "Write a hello world web application using Rack and deploy it on Clever Cloud" icon="ruby" >}} + {{< card link="/developers/guides/sveltekit" title="SvelteKit" subtitle="Deploy a SvelteKit application with static or server-side rendering" icon="svelte" >}} {{< card link="/developers/guides/tutorial-symfony" title="Symfony" subtitle= "Deploy a Symfony application on Clever Cloud" icon="symfony" >}} {{< card link="/developers/guides/tutorial-wordpress" title="WordPress" subtitle= "Deploy WordPress on Clever Cloud" icon="wordpress" >}} {{< /cards >}} diff --git a/content/guides/sveltekit.md b/content/guides/sveltekit.md new file mode 100644 index 000000000..3fd04c0d5 --- /dev/null +++ b/content/guides/sveltekit.md @@ -0,0 +1,232 @@ +--- +type: docs +linkTitle: SvelteKit +title: SvelteKit +description: Deploy a SvelteKit application as a static site, a single-page application, or a Node.js server on Clever Cloud +keywords: +- sveltekit +- svelte +- static site generation +- single-page application +- Node.js +- JavaScript framework +- server-side rendering +--- + +{{< hextra/hero-subtitle >}} + SvelteKit is a full-stack web framework for Svelte, with support for static site generation, single-page applications, and server-side rendering. +{{< /hextra/hero-subtitle >}} + +Clever Cloud supports all three SvelteKit rendering modes. Static site generation (SSG) and single-page applications (SPA) use [@sveltejs/adapter-static](https://svelte.dev/docs/kit/adapter-static) and the [Static runtime](/developers/doc/applications/static/). Server-side rendering (SSR) uses [@sveltejs/adapter-node](https://svelte.dev/docs/kit/adapter-node) and the [Node.js runtime](/developers/doc/applications/nodejs/). + +SSG is well suited to content-oriented sites whose pages do not change for each visitor. SPA works well for interactive applications where search engine indexing is not a priority. Use SSR when pages depend on real-time data or user-specific state. + +## Create a SvelteKit project + +You need [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/en/download) to follow this guide. Create a minimal TypeScript project: + +```bash +npx sv create --template minimal --types ts --no-add-ons --install npm mySvelteApp +cd mySvelteApp +``` + +If you already have a SvelteKit project, run the following commands from its root directory. + +## Deploy as a static site + +Both SSG and SPA modes use `@sveltejs/adapter-static` and the Clever Cloud Static runtime. SSG generates an HTML file for each route at build time. SPA generates a single HTML shell and handles routing in the browser. + +Install the Static adapter: + +```bash +npm install --save-dev @sveltejs/adapter-static +``` + +Then choose one of the following rendering modes. + +### Static site generation + +Configure the adapter in `vite.config.ts`: + +```typescript {filename="vite.config.ts"} +import adapter from '@sveltejs/adapter-static'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + compilerOptions: { + // Force runes mode for the project, except for libraries. Can be removed in Svelte 6. + runes: ({ filename }) => + filename.split(/[/\\]/).includes('node_modules') ? undefined : true + }, + adapter: adapter() + }) + ] +}); +``` + +Enable prerendering in the root layout so SvelteKit generates static HTML for every route: + +```typescript {filename="src/routes/+layout.ts"} +export const prerender = true; +``` + +### Single-page application + +SPA mode has performance and search engine optimization drawbacks. Prefer SSG or SSR unless your application specifically needs client-only rendering. + +Disable server-side rendering in the root layout: + +```typescript {filename="src/routes/+layout.ts"} +export const ssr = false; +``` + +Then configure a fallback page in `vite.config.ts`: + +```typescript {filename="vite.config.ts"} +import adapter from '@sveltejs/adapter-static'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + compilerOptions: { + // Force runes mode for the project, except for libraries. Can be removed in Svelte 6. + runes: ({ filename }) => + filename.split(/[/\\]/).includes('node_modules') ? undefined : true + }, + adapter: adapter({ fallback: '200.html' }) + }) + ] +}); +``` + +### Create a Static application + +Install [Clever Tools](/developers/doc/cli/), log in, initialize the Git repository, and create a Static application: + +```bash +npm i -g clever-tools +clever login + +git init +clever create -t static +``` + +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + +To use an existing application instead, link it to the current repository: + +```bash +clever link your_app_name_or_ID +``` + +### Configure the build + +Set the output directory and the command Clever Cloud runs during the build phase: + +```bash +clever env set CC_WEBROOT "/build" +clever env set CC_BUILD_COMMAND "npm install && npm run build" +``` + +For SPA mode, also tell the default [Static Web Server](https://static-web-server.net/features/error-pages/#fallback-page-for-use-with-client-routers) to serve the generated fallback for routes that do not match a file: + +```bash +clever env set SERVER_FALLBACK_PAGE "./build/200.html" +``` + +Your Static application is ready. Continue to [deploy the application](#deploy-the-application). + +## Deploy with server-side rendering + +SSR renders pages on each request using a Node.js server. Install `@sveltejs/adapter-node`, which generates a standalone server in the `build/` directory: + +```bash +npm install --save-dev @sveltejs/adapter-node +``` + +Configure the adapter in `vite.config.ts`: + +```typescript {filename="vite.config.ts"} +import adapter from '@sveltejs/adapter-node'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + compilerOptions: { + // Force runes mode for the project, except for libraries. Can be removed in Svelte 6. + runes: ({ filename }) => + filename.split(/[/\\]/).includes('node_modules') ? undefined : true + }, + adapter: adapter() + }) + ] +}); +``` + +### Create a Node.js application + +Install [Clever Tools](/developers/doc/cli/), log in, initialize the Git repository, and create a Node.js application: + +```bash +npm i -g clever-tools +clever login + +git init +clever create -t node +``` + +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + +To use an existing application instead, link it to the current repository: + +```bash +clever link your_app_name_or_ID +``` + +### Configure the application + +Configure Clever Cloud to install the development dependencies needed by the build, run that build, and start the generated Node.js server: + +```bash +clever env set CC_NODE_DEV_DEPENDENCIES install +clever env set CC_POST_BUILD_HOOK "npm run build" +clever env set CC_RUN_COMMAND "node build" +clever env set PROTOCOL_HEADER "x-forwarded-proto" +clever env set HOST_HEADER "host" +``` + +Clever Cloud provides the `PORT` environment variable expected by `adapter-node`. `PROTOCOL_HEADER` and `HOST_HEADER` let SvelteKit reconstruct the public URL from requests forwarded by Clever Cloud, including requests made through a [custom domain](/developers/doc/administrate/domain-names/). + +## Deploy the application + +Commit the project and deploy it: + +```bash +git add . +git commit -m "First deploy" + +clever deploy +clever open +``` + +You can display the application's domains or add a [custom domain](/developers/doc/administrate/domain-names/): + +```bash +clever domain +clever domain add your.website.tld +``` + +## Learn more + +{{< cards >}} + {{< card link="/developers/doc/applications/nodejs/" title="Node.js applications" subtitle="Configure and deploy Node.js applications" icon="node" >}} + {{< card link="/developers/doc/applications/static/" title="Static applications" subtitle="Configure and deploy static applications" icon="static" >}} + {{< card link="https://svelte.dev/docs/kit/adapters" title="SvelteKit adapters" subtitle="Choose and configure a SvelteKit adapter" icon="svelte" >}} +{{< /cards >}} diff --git a/data/icons.yaml b/data/icons.yaml index 022e58b60..257902bc1 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -98,6 +98,7 @@ new-relic: # Frameworks meteor: +svelte: maven: moodle: docs-suite: diff --git a/shared/static-deploy.md b/shared/static-deploy.md index d10988497..64736ceca 100644 --- a/shared/static-deploy.md +++ b/shared/static-deploy.md @@ -5,6 +5,7 @@ Once you complete these steps, commit your content to the local repository and d ```bash git add . git commit -m "First deploy" + clever deploy clever open ``` From c46ea39e7ba4821f902234c814ff8d55200c87eb Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 24 Aug 2026 18:07:10 +0200 Subject: [PATCH 109/214] applications(static): fix Static Web Server documentation links --- content/doc/applications/static.md | 6 +++--- content/guides/sveltekit.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/doc/applications/static.md b/content/doc/applications/static.md index 43cc854e9..ac0bd5dc4 100644 --- a/content/doc/applications/static.md +++ b/content/doc/applications/static.md @@ -84,9 +84,9 @@ Caddy and SWS can be configured with a configuration file or through environment - [Environment Variables](https://caddyserver.com/docs/caddyfile/concepts#environment-variables) - [Caddyfile](https://caddyserver.com/docs/caddyfile) - Static Web Server (SWS) configuration: - - [Command Line Arguments](https://static-web-server.net/configuration/command-line-arguments/) - - [Environment Variables](https://static-web-server.net/configuration/environment-variables/) - - [TOML Configuration File](https://static-web-server.net/configuration/config-file/) + - [Command Line Arguments](https://static-web-server.net/v2/configuration/command-line-arguments/) + - [Environment Variables](https://static-web-server.net/v2/configuration/environment-variables/) + - [TOML Configuration File](https://static-web-server.net/v2/configuration/config-file/) ## Static Site Generators (SSG) Auto-build diff --git a/content/guides/sveltekit.md b/content/guides/sveltekit.md index 3fd04c0d5..1f129b105 100644 --- a/content/guides/sveltekit.md +++ b/content/guides/sveltekit.md @@ -133,7 +133,7 @@ clever env set CC_WEBROOT "/build" clever env set CC_BUILD_COMMAND "npm install && npm run build" ``` -For SPA mode, also tell the default [Static Web Server](https://static-web-server.net/features/error-pages/#fallback-page-for-use-with-client-routers) to serve the generated fallback for routes that do not match a file: +For SPA mode, also tell the default [Static Web Server](https://static-web-server.net/v2/features/error-pages/#fallback-page-for-use-with-client-routers) to serve the generated fallback for routes that do not match a file: ```bash clever env set SERVER_FALLBACK_PAGE "./build/200.html" From 962470e33dd85eb59d5e05ca3c50ef0575cf969d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Ollivier?= Date: Tue, 16 Jun 2026 19:09:38 +0200 Subject: [PATCH 110/214] guides(nuxt): fix deployment instructions Co-authored-by: David Legrand --- content/guides/nuxt.md | 48 ++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/content/guides/nuxt.md b/content/guides/nuxt.md index d4c9c64c7..e4af0ec30 100644 --- a/content/guides/nuxt.md +++ b/content/guides/nuxt.md @@ -8,7 +8,7 @@ keywords: - Vue.js - static site generator - Node.js -- javascript framework +- JavaScript framework aliases: - /doc/nuxt - /nuxt @@ -18,12 +18,12 @@ aliases: Nuxt is a powerful and versatile web framework for building modern web applications, offering a seamless development experience with server-side rendering, static site generation, and hybrid capabilities. {{< /hextra/hero-subtitle >}} -Clever Cloud supports deploying both [fully static and on-demand rendered](https://nuxt.com/docs/guide/concepts/rendering) Nuxt 4 projects: +Clever Cloud supports Nuxt 4 projects as pre-rendered static sites or [on-demand rendered](https://nuxt.com/docs/4.x/guide/concepts/rendering) applications: -- The `static` rendering mode is ideal for most content-oriented websites where you don't need per-visitor server-side customization. Consider using a [Static runtime](/doc/applications/static/) when using this mode, with the automatic site generation. Learn more about [static hosting with Nuxt](https://nuxt.com/docs/getting-started/deployment#static-hosting). -- The `server` or `hybrid` rendering modes are optimal for dynamic applications requiring server-side customization or a mix of static and server-rendered pages. For these modes, consider using a [Node.js runtime](/doc/applications/nodejs). Learn more about [Node.js hosting with Nuxt](https://nuxt.com/docs/getting-started/deployment#nodejs-server). +- A pre-rendered site is ideal for most content-oriented websites where you don't need per-visitor server-side customization. Use the [Static runtime](/developers/doc/applications/static/) to generate and serve it automatically. Learn more about [static hosting with Nuxt](https://nuxt.com/docs/4.x/getting-started/deployment#static-hosting). +- Server-side or hybrid rendering is better suited to dynamic applications requiring server-side customization or a mix of static and server-rendered pages. Use the [Node.js runtime](/developers/doc/applications/nodejs/) for these modes. Learn more about [Node.js hosting with Nuxt](https://nuxt.com/docs/4.x/getting-started/deployment#nodejs-server). -If you need an example source code compatible with Nuxt static generation, get [Nuxt Boilerplate](https://github.com/renegadevi/nuxt-boilerplate) (you'll need [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Node.js](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs)): +To follow this guide, clone [Nuxt Boilerplate](https://github.com/renegadevi/nuxt-boilerplate), then choose one of the two deployment methods below. You need [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Node.js](https://nodejs.org/en/download): ```bash git clone https://github.com/renegadevi/nuxt-boilerplate myStaticApp @@ -31,26 +31,21 @@ git clone https://github.com/renegadevi/nuxt-boilerplate myStaticApp {{% content "static-create" %}} -### Automatic build - -Nuxt is one of the many Static Site Generator (SSG) that [Clever Cloud automatic build](/doc/applications/static/#static-site-generators-ssg-auto-build) supports in the `static` runtime. Then, you don't have anything special to manage. To use a pico instance with a dedicated build instance change it in the [Console](https://console.clever-cloud.com) or with Clever Tools: +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. -```bash -clever scale --flavor pico +### Automatic build -# To select a bigger build instance, use: -clever scale --build-flavor M -``` +The Static runtime automatically detects `nuxt.config.ts`, runs the Nuxt static generation command and serves the generated files. {{% content "static-deploy" %}} ## Deploy a Node.js application -To deploy your Nuxt project to Clever Cloud, you need to **create a new application**. +Use a Node.js application for Nuxt server-side rendering and hybrid rendering. ### Create a Node.js application -You can create an application from the [Console](https://console.clever-cloud.com) or through [Clever Tools](https://github.com/CleverCloud/clever-tools/): +Install [Clever Tools](/developers/doc/cli/), log in and create a Node.js application linked to the cloned repository: ```bash npm i -g clever-tools @@ -58,26 +53,24 @@ clever login cd myStaticApp clever create -t node -``` -To deploy on Clever Cloud, your local folder needs to be a git repository (if not, `git init`) linked to an application. If you already have an application on Clever Cloud and want to link it to the current local folder: - -```bash +# Or link an existing Clever Cloud application: clever link your_app_name_or_ID ``` +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + ### Environment variables -Next, configure the application with a medium build instance. The host instance is XS, enough for a simple website. As Clever Cloud uses industry standards, you only need to define a few variables: +Install the development dependencies required by Nuxt and build the server after dependency installation: ```bash -clever scale --build-flavor M -clever scale --flavor XS - -clever env set CC_PRE_BUILD_HOOK "npm run build" -clever env set CC_RUN_COMMAND "node .output/server/index.mjs" +clever env set CC_NODE_DEV_DEPENDENCIES install +clever env set CC_POST_BUILD_HOOK "npm run build" ``` +The Node.js runtime installs the dependencies before running the post-build hook. It then starts the application with the `start` script from `package.json`, which runs the generated `.output/server/index.mjs` server. Nuxt reads the `PORT` environment variable injected by Clever Cloud, so you don't need to configure a listening port. + ### Push your code Once you complete these steps, commit your content to the local repository and deploy it: @@ -85,11 +78,12 @@ Once you complete these steps, commit your content to the local repository and d ```bash git add . git commit -m "First deploy" + clever deploy clever open ``` -You can display your website's URL or add a custom domain to it (you'll need to configure DNS): +You can display your application's domains or add a [custom domain](/developers/doc/administrate/domain-names/): ```bash clever domain @@ -101,5 +95,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/nodejs" title="Deploy a Node.js application" subtitle="Learn more on deploying a Node.js application" icon="node" >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="Learn more on deploying a Static application" icon="static" >}} - {{< card link="https://nuxt.com/docs" title="Learn Nuxt" subtitle="How to write and organize your content" icon="nuxt" >}} + {{< card link="https://nuxt.com/docs/4.x/getting-started/introduction" title="Learn Nuxt" subtitle="How to write and organize your content" icon="nuxt" >}} {{< /cards >}} From 01f06f6b0a2d0c1e9da7b240a0fa0589ff913daa Mon Sep 17 00:00:00 2001 From: Horacio Gonzalez Date: Thu, 21 Aug 2025 14:55:52 +0200 Subject: [PATCH 111/214] guides: add Outline Co-authored-by: David Legrand --- content/guides/_index.md | 1 + content/guides/outline.md | 279 ++++++++++++++++++++++++++++++++++++++ data/icons.yaml | 1 + 3 files changed, 281 insertions(+) create mode 100644 content/guides/outline.md diff --git a/content/guides/_index.md b/content/guides/_index.md index a7dfd8604..f25977803 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -49,6 +49,7 @@ llmsTxtOptional: true {{< card link="/developers/guides/node-js-mongo-db" title="Node.js + MongoDB starter" subtitle= "Starter tutorial to deploy a Node.js + MongoDB application on Clever Cloud" icon="node" >}} {{< card link="/developers/guides/node-statsd-nodejs-metrics" title="node-statsd" subtitle= "Configure node-statsd package on your Node.js application to push custom metrics" icon="node" >}} {{< card link="/developers/guides/nuxt" title="Nuxt" subtitle= "Build and deploy a Nuxt application on Clever Cloud" icon="nuxt" >}} + {{< card link="/developers/guides/outline" title="Outline" subtitle= "Deploy the Outline knowledge base on Clever Cloud" icon="outline" >}} {{< card link="/developers/guides/pgpool" title="Pgpool-II" subtitle= "How to configure and use Pgpool-II for PostgreSQL add-ons" icon="pg" >}} {{< card link="/developers/guides/proxysql" title="ProxySQL" subtitle= "Configure and use ProxySQL for MySQL add-ons" icon="mysql" >}} {{< card link="/developers/guides/ruby-on-rails" title="Ruby On Rails" subtitle= "How to deploy Ruby on Rails framework" icon="ruby" >}} diff --git a/content/guides/outline.md b/content/guides/outline.md new file mode 100644 index 000000000..1e4326c78 --- /dev/null +++ b/content/guides/outline.md @@ -0,0 +1,279 @@ +--- +type: docs +linkTitle: Outline +title: Deploy self-hosted Outline +description: Deploy the Outline knowledge base on Clever Cloud with PostgreSQL, Redis, and Cellar object storage +keywords: +- outline +- knowledge base +- wiki +- node.js +- postgresql +- redis +- cellar +--- + +{{< hextra/hero-subtitle >}} + Deploy Outline, an open source knowledge base for teams, on Clever Cloud. +{{< /hextra/hero-subtitle >}} + +[Outline](https://www.getoutline.com/) is a collaborative knowledge base and wiki. This guide deploys it on the [Linux runtime](/developers/doc/applications/linux/) with [Mise](https://mise.jdx.dev/), [PostgreSQL](/developers/doc/addons/postgresql/), [Redis](/developers/doc/addons/redis/), and [Cellar S3-compatible object storage](/developers/doc/addons/cellar/). Outline recommends Docker for self-hosted installations, but it also [documents installation from source](https://docs.getoutline.com/s/hosting/doc/from-source-BlBxrNzMIP). + +## Prepare the application + +You need [Git](https://git-scm.com/downloads), [OpenSSL](https://openssl-library.org/), and an S3-compatible client to follow this guide. The commands below use [s3cmd](https://s3tools.org/s3cmd), but you can adapt them to another S3-compatible client. + +Create a directory for the deployment configuration: + +```bash +mkdir myOutline +cd myOutline +git init +``` + +This guide targets Outline `v1.9.2`. To deploy another Outline release, adjust `OUTLINE_VERSION` and `CC_NODE_VERSION` as described below. + +[Mise is available on Clever Cloud](/developers/doc/reference/reference-environment-variables/#install-tools-with-mise-package-manager). Create the following `mise.toml` file to download the selected Outline release during the build phase, install its dependencies, and define the commands used by the Linux runtime: + +```toml {filename="mise.toml"} +[tools."http:outline"] +version = "{{ get_env(name='OUTLINE_VERSION', default='v1.9.2') }}" +url = "https://github.com/outline/outline/archive/refs/tags/{{ version }}.tar.gz" +strip_components = 1 + +[tasks.build] +description = "Build the selected Outline release" +run = ''' +set -euo pipefail +mkdir outline +cp -a '{{ tools["http:outline"].path }}/.' outline/ +cd outline +corepack enable +yarn install --immutable +yarn build +yarn workspaces focus --production +yarn cache clean +''' + +[tasks.run] +description = "Start Outline" +run = "bash run.sh" +``` + +The [HTTP backend](https://mise.jdx.dev/dev-tools/backends/http.html) downloads and extracts the source archive. The `build` and `run` names are significant: the Linux runtime automatically uses these [Mise tasks](https://mise.jdx.dev/tasks/) for its separate build and run phases. + +Create a startup script that maps the variables injected by linked Clever Cloud add-ons to the names expected by Outline: + +```bash {filename="run.sh"} +#!/usr/bin/env bash +set -euo pipefail + +export DATABASE_URL="${POSTGRESQL_ADDON_URI}" +export REDIS_COLLABORATION_URL="${REDIS_URL}" +export AWS_ACCESS_KEY_ID="${CELLAR_ADDON_KEY_ID}" +export AWS_SECRET_ACCESS_KEY="${CELLAR_ADDON_KEY_SECRET}" +export AWS_S3_UPLOAD_BUCKET_URL="https://${CELLAR_ADDON_HOST}" + +cd outline +node build/server/index.js +``` + +The linked Redis add-on provides `REDIS_URL`, which the script also maps to `REDIS_COLLABORATION_URL` to synchronize collaborative editing between Outline processes and application instances. Outline automatically applies pending database migrations when it starts, so no separate migration command is needed. + +## Create the application and add-ons + +Install [Clever Tools](/developers/doc/cli/), log in, and create a Linux application with an alias used to link the add-ons: + +```bash +npm i -g clever-tools +clever login + +clever create -t linux -a myOutline +``` + +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + +You can display your application's URL or add a custom domain. A custom domain also requires [DNS configuration](/developers/doc/administrate/domain-names/): + +```bash +clever domain +clever domain add your.website.tld +``` + +Create PostgreSQL, Redis, and Cellar add-ons, then link them to the application: + +```bash +clever addon create postgresql-addon myOutlinePostgreSQL -p xxs_sml --link myOutline +clever addon create redis-addon myOutlineRedis -p s_mono --link myOutline +clever addon create cellar-addon myOutlineCellar --link myOutline +``` + +You can also create and link these resources from the [Clever Cloud Console](https://console.clever-cloud.com/). + +## Configure Cellar + +Outline stores uploaded files in a private S3 bucket and gives authenticated users temporary download URLs. Bucket names are global within a Cellar cluster, so choose a unique name containing only lowercase letters, numbers, dots, and hyphens: + +```bash +export OUTLINE_BUCKET="your-unique-outline-bucket" +``` + +Export only the linked Cellar add-on variables to your current shell, then create the bucket: + +```bash +source <(clever env -F shell | grep '^export CELLAR_ADDON_') + +s3cmd --access_key="$CELLAR_ADDON_KEY_ID" \ + --secret_key="$CELLAR_ADDON_KEY_SECRET" \ + --host="$CELLAR_ADDON_HOST" \ + --host-bucket="$CELLAR_ADDON_HOST" \ + --ssl mb "s3://$OUTLINE_BUCKET" +``` + +Direct uploads from the browser require a [CORS policy](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing). Display the application domain to get the exact HTTPS origin: + +```bash +clever domain +``` + +Create the following file and replace `https://your-outline-domain.example.com` with that origin. Browser write requests are allowed from your Outline application's origin, while downloads can use the temporary URLs generated by Outline: + +```xml {filename="cors.xml"} + + + https://your-outline-domain.example.com + PUT + POST + * + + + * + GET + + +``` + +Apply the policy, then remove the credentials from your shell. Keep the `OUTLINE_BUCKET` value exported for use in the next section: + +```bash +s3cmd --access_key="$CELLAR_ADDON_KEY_ID" \ + --secret_key="$CELLAR_ADDON_KEY_SECRET" \ + --host="$CELLAR_ADDON_HOST" \ + --host-bucket="$CELLAR_ADDON_HOST" \ + --ssl setcors cors.xml "s3://$OUTLINE_BUCKET" + +rm cors.xml +unset CELLAR_ADDON_KEY_ID CELLAR_ADDON_KEY_SECRET CELLAR_ADDON_HOST +``` + +The bucket remains private: Outline signs file downloads, so it does not need a public-read bucket policy. + +## Configure Outline + +Store the application domain in a shell variable (`app-id.cleverapps.io` or your custom domain): + +```bash +export OUTLINE_URL="https://your-outline-domain.example.com" +``` + +Generate two independent secrets and configure the Outline release, Node.js, public URL, and object storage. The `CC_NODE_VERSION` value must match one of the ranges accepted by `engines.node` in the `package.json` of the selected Outline release: + +```bash +# Use a value accepted by engines.node for the selected Outline release: +# https://github.com/outline/outline/blob/v1.9.2/package.json#L44-L46 +clever env set OUTLINE_VERSION v1.9.2 +clever env set CC_NODE_VERSION 26.3.0 +clever env set CC_NODE_BUILD_TOOL yarn-berry + +clever env set SECRET_KEY "$(openssl rand -hex 32)" +clever env set UTILS_SECRET "$(openssl rand -base64 32)" +clever env set NODE_ENV production +clever env set WEB_CONCURRENCY 1 +clever env set DEFAULT_LANGUAGE en_US +clever env set URL "$OUTLINE_URL" +clever env set FILE_STORAGE s3 +clever env set FILE_STORAGE_UPLOAD_MAX_SIZE 262144000 +clever env set AWS_REGION default +clever env set AWS_S3_UPLOAD_BUCKET_NAME "$OUTLINE_BUCKET" +``` + +Outline requires `SECRET_KEY` to contain exactly 64 hexadecimal characters, while `UTILS_SECRET` accepts the Base64 value. Clever Cloud provides the application `PORT`. The linked add-ons provide their credentials, so they do not need to be copied into the application environment. + +`WEB_CONCURRENCY` controls the number of Outline processes started inside each application instance. Outline recommends roughly one process per 512 MB of available memory, so this guide keeps the value at `1` for the default `XS` run instance. Increase it only on a larger instance and after monitoring the application's memory and CPU usage. The `REDIS_COLLABORATION_URL` mapping in the startup script follows Outline's [horizontal scaling documentation](https://docs.getoutline.com/s/hosting/doc/horizontal-scaling-hkfU5Stao7), so collaborative editing remains synchronized if you later run several processes or application instances. + +`DEFAULT_LANGUAGE` controls the default interface language; replace `en_US` with another [Outline language code](https://translate.getoutline.com/) if needed. `FILE_STORAGE_UPLOAD_MAX_SIZE` is expressed in bytes: `262144000` sets a 250 MiB attachment limit and matches Outline's [versioned environment sample](https://github.com/outline/outline/blob/v1.9.2/.env.sample). You can configure a larger value, but Outline applies it to the [`content-length-range` condition of each presigned S3 upload](https://github.com/outline/outline/blob/v1.9.2/server/storage/files/S3Storage.ts#L44-L67), so test the intended size with your network and storage service before increasing it. Document and workspace imports can use separate limits; see Outline's [file storage documentation](https://docs.getoutline.com/s/hosting/doc/file-storage-N4M0T6Ypu7). + +Outline's dependency installation and client build need more memory than the default build instance provides. Use an `M` instance for the build phase; the application keeps the default `XS` instance for its runtime: + +```bash +clever scale --build-flavor M +``` + +### Configure authentication + +Outline needs at least one authentication method before users can sign in and supports the following methods: + +| Authentication method | Required variables | Callback URL | +| --- | --- | --- | +| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET` | `$URL/auth/discord.callback` | +| Email magic links | `SMTP_HOST` or `SMTP_SERVICE`, `SMTP_FROM_EMAIL` | — | +| Google | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | `$URL/auth/google.callback` | +| Microsoft Entra ID | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET` | `$URL/auth/azure.callback` | +| OpenID Connect | `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, `OIDC_ISSUER_URL` | `$URL/auth/oidc.callback` | +| Slack | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET` | `$URL/auth/slack.callback` | + +Email magic links require a complete [SMTP configuration](https://docs.getoutline.com/s/hosting/doc/smtp-cqCJyZGMIB), including the credentials and connection settings required by your email provider. Configure the matching callback URL in your provider, then set its variables. For OpenID Connect, also allow the application URL (`$URL`) as a post-logout redirect URI if the provider exposes a logout endpoint. For example: + +```bash +clever env set OIDC_CLIENT_ID "YOUR_CLIENT_ID" +clever env set OIDC_CLIENT_SECRET "A_STRONG_CLIENT_SECRET" +clever env set OIDC_ISSUER_URL "https://your-provider.example.com" +clever env set OIDC_DISPLAY_NAME "Company SSO" +``` + +Once signed in, users can also register passkeys for subsequent authentication. The first user to create an Outline workspace becomes its administrator. SAML authentication is only available in licensed editions. + +## Deploy Outline + +Commit the deployment configuration and deploy the application: + +```bash +git add mise.toml run.sh +git commit -m "First deploy" + +clever deploy +``` + +Outline builds its client and server during the build phase, then starts and applies pending PostgreSQL migrations during the run phase. Check that PostgreSQL and Redis are reachable with its health endpoint: + +```bash +curl "$OUTLINE_URL/_health" +``` + +It returns `OK` when both dependencies are available. You can then open Outline and sign in with the authentication provider you configured: + +```bash +clever open +``` + +## Update Outline + +Make sure you have a recent [PostgreSQL backup](/developers/doc/addons/postgresql/#database-daily-backup-and-retention) before updating. Check the `engines.node` requirements in the `package.json` of the Outline release you want to deploy, such as the [file for v1.9.2](https://github.com/outline/outline/blob/v1.9.2/package.json#L44-L46). Update both version variables when necessary, then rebuild the current commit so Mise downloads and builds the new release. For example, these commands update an older deployment to Outline v1.9.2: + +```bash +clever env set OUTLINE_VERSION v1.9.2 +clever env set CC_NODE_VERSION 26.3.0 +clever restart --without-cache +``` + +Replace the example values with the versions you selected. Outline applies pending database migrations when the new version starts. + +## Learn more + +{{< cards >}} + {{< card link="/developers/doc/applications/linux" title="Linux application runtime" subtitle="Configure and deploy any application" icon="linux" >}} + {{< card link="/developers/doc/addons/postgresql" title="PostgreSQL" subtitle="Explore managed PostgreSQL databases" icon="pg" >}} + {{< card link="/developers/doc/addons/redis" title="Redis" subtitle="Explore managed Redis databases" icon="redis" >}} + {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="Explore S3-compatible object storage" icon="fsbucket" >}} + {{< card link="https://docs.getoutline.com/s/hosting" title="Outline hosting documentation" subtitle="Learn more about self-hosting Outline" icon="outline" >}} +{{< /cards >}} diff --git a/data/icons.yaml b/data/icons.yaml index 257902bc1..f8e3fc74e 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -101,6 +101,7 @@ meteor: maven: moodle: +outline: docs-suite: drupal: From bbee0d0b0c472346532815813a196e8147545288 Mon Sep 17 00:00:00 2001 From: Julie POUNY Date: Wed, 6 May 2026 18:16:57 +0200 Subject: [PATCH 112/214] guides: add SFTPGo Co-Authored-By: David Legrand --- content/doc/addons/fs-bucket.md | 2 + content/guides/_index.md | 1 + content/guides/sftpgo.md | 275 ++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 content/guides/sftpgo.md diff --git a/content/doc/addons/fs-bucket.md b/content/doc/addons/fs-bucket.md index e394ed13c..e563c6282 100644 --- a/content/doc/addons/fs-bucket.md +++ b/content/doc/addons/fs-bucket.md @@ -156,6 +156,8 @@ The **Add-on information** tab of your FS Bucket in [Clever Cloud Console](https | RSA | 4096 | `SHA256:yRHC/tAlBpHLlRZ5rwbZ1z+159Bj3yg0VxHf+hXINLg` | | RSA | 4096 | `SHA256:yhn79aqxOGQZ+LXdN1/vIY+jwRIbBamlVT1+HdFoA6o` | +FS Buckets use Clever Cloud-managed SFTP credentials and do not support custom SSH keys or separate user accounts; for these features, deploy [SFTPGo](/guides/sftpgo/) instead. + ### From your favorite FTP client The **Add-on information** tab of your FS Bucket add-on displays the information diff --git a/content/guides/_index.md b/content/guides/_index.md index f25977803..a4be1e7e1 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -55,6 +55,7 @@ llmsTxtOptional: true {{< card link="/developers/guides/ruby-on-rails" title="Ruby On Rails" subtitle= "How to deploy Ruby on Rails framework" icon="ruby" >}} {{< card link="/developers/guides/ruby-rack" title="Ruby Rack" subtitle= "Set up a Ruby Rack application and deploy on Clever Cloud" icon="ruby" >}} {{< card link="/developers/guides/ruby-rack-app-tutorial" title="Ruby Rack (full tutorial)" subtitle= "Write a hello world web application using Rack and deploy it on Clever Cloud" icon="ruby" >}} + {{< card link="/developers/guides/sftpgo" title="SFTPGo" subtitle="Deploy an SFTP server with PostgreSQL and Cellar object storage" icon="server-stack" >}} {{< card link="/developers/guides/sveltekit" title="SvelteKit" subtitle="Deploy a SvelteKit application with static or server-side rendering" icon="svelte" >}} {{< card link="/developers/guides/tutorial-symfony" title="Symfony" subtitle= "Deploy a Symfony application on Clever Cloud" icon="symfony" >}} {{< card link="/developers/guides/tutorial-wordpress" title="WordPress" subtitle= "Deploy WordPress on Clever Cloud" icon="wordpress" >}} diff --git a/content/guides/sftpgo.md b/content/guides/sftpgo.md new file mode 100644 index 000000000..e7789347e --- /dev/null +++ b/content/guides/sftpgo.md @@ -0,0 +1,275 @@ +--- +type: docs +linkTitle: SFTPGo +title: Deploy an SFTP server with SFTPGo +description: Deploy SFTPGo on Clever Cloud with the Linux runtime, PostgreSQL and Cellar object storage +keywords: +- sftpgo +- sftp +- file transfer +- linux +- mise +- postgresql +- cellar +- s3 storage +- file management +--- + +{{< hextra/hero-subtitle >}} + SFTPGo is an open-source SFTP server with multi-user management, a web interface and support for S3-compatible storage backends such as Cellar. +{{< /hextra/hero-subtitle >}} + +Clever Cloud provides built-in SFTP access to [FS Buckets](/developers/doc/addons/fs-bucket/). Deploy SFTPGo when you need advanced user management or fine-grained access control, or when you want to expose files stored in [Cellar](/developers/doc/addons/cellar/) over SFTP. This guide uses the [Linux runtime](/developers/doc/applications/linux/) and [Mise](https://mise.jdx.dev/) to install SFTPGo, PostgreSQL to persist its configuration, and Cellar for object storage. + +This guide only covers SFTP. FTP requires a dedicated TCP port range. Contact [Clever Cloud support](https://console.clever-cloud.com/ticket/center) if you need it. + +## Prepare the SFTPGo project + +You need [Git](https://git-scm.com/downloads), [OpenSSL](https://openssl-library.org/) and a [Clever Cloud account](https://console.clever-cloud.com) to follow this guide. Create a project and initialize its Git repository: + +```bash +mkdir mySftpGo +cd mySftpGo +git init +``` + +[Mise](https://mise.jdx.dev/) is available on Clever Cloud. Declare dependencies, environment variables and tasks in a `mise.toml` file. Here, use it to install the latest stable SFTPGo release from GitHub and define its run task: + +```toml {filename="mise.toml"} +[settings.github] +github_attestations = false + +[tools] +# Replace "latest" with a specific release such as "2.7.5" to pin SFTPGo. +"github:drakkan/sftpgo" = "latest" + +[env] +SFTPGO_DATA_PROVIDER__DRIVER = "postgresql" +SFTPGO_DATA_PROVIDER__CONNECTION_STRING = "{{ env.POSTGRESQL_ADDON_URI }}" +SFTPGO_HTTPD__TEMPLATES_PATH = { value = "{{ tools['github:drakkan/sftpgo'].path }}/templates", tools = true } +SFTPGO_HTTPD__STATIC_FILES_PATH = { value = "{{ tools['github:drakkan/sftpgo'].path }}/static", tools = true } +SFTPGO_HTTPD__OPENAPI_PATH = { value = "{{ tools['github:drakkan/sftpgo'].path }}/openapi", tools = true } +SFTPGO_SMTP__TEMPLATES_PATH = { value = "{{ tools['github:drakkan/sftpgo'].path }}/templates", tools = true } +SFTPGO_SFTPD__BINDINGS__0__PORT = "4040" +SFTPGO_RUNTIME_DIR = "/tmp/sftpgo" +SFTPGO_SFTPD__HOST_KEYS = "{{ env.SFTPGO_RUNTIME_DIR }}/id_ed25519" +SFTPGO_LOG_FILE_PATH = "" +SFTPGO_LOG_LEVEL = "info" + +[tasks.run] +run = ''' +#!/usr/bin/env bash + +set -euo pipefail + +: "${SFTPGO_SSH_HOST_KEY_B64:?Configure the persistent SFTP host key before deploying}" +mkdir -p "$SFTPGO_RUNTIME_DIR" +chmod 700 "$SFTPGO_RUNTIME_DIR" +printf '%s' "${SFTPGO_SSH_HOST_KEY_B64}" | openssl base64 -d -A > "$SFTPGO_SFTPD__HOST_KEYS" +chmod 600 "$SFTPGO_SFTPD__HOST_KEYS" + +sftpgo serve +''' +``` + +The project-level `github_attestations` setting works around an incompatibility between the GitHub attestation currently published for SFTPGo and the version of Mise available on Clever Cloud. Mise still verifies the downloaded asset checksum. + +The name `run` is significant: unless you define `CC_RUN_COMMAND`, the Linux runtime [detects and executes](/developers/doc/applications/linux/#build-and-run-commands) the [Mise task](https://mise.jdx.dev/tasks/) as the application run command. It restores the persistent SFTP host key, prepares the temporary directory and starts the SFTPGo server. The environment maps the PostgreSQL add-on URI injected by Clever Cloud to SFTPGo and points to the web resources installed by Mise. + +SFTPGo listens on port `8080` for its web interface by default. The configuration binds its SFTP server to port `4040`, used by Clever Cloud TCP redirections. It sends logs to the standard output with `info` level because SFTPGo's default `debug` logs can include the PostgreSQL URI. + +## Create and configure the Clever Cloud resources + +Install [Clever Tools](/developers/doc/cli/), log in and create a Linux application with an alias: + +```bash +npm i -g clever-tools +clever login + +clever create -t linux -a mySftpGo +``` + +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + +Use the application alias to create and link a PostgreSQL add-on and a Cellar add-on. The native links automatically inject the PostgreSQL and Cellar environment variables into the application: + +```bash +clever addon create postgresql-addon mySftpGoPg -p xxs_sml -l mySftpGo +clever addon create cellar-addon mySftpGoCellar -l mySftpGo +``` + +You can alternatively create the application and add-ons, and link the add-ons to the application, from the [Clever Cloud Console](https://console.clever-cloud.com/). + +Finally, create the TCP redirection used for SFTP connections: + +```bash +clever tcp-redirs add --namespace cleverapps +``` + +The command returns the public TCP port to use with your application's `.cleverapps.io` domain. To use a custom SFTP hostname, follow the [TCP redirection documentation](/developers/doc/administrate/tcp-redirections/) and create the redirection in the appropriate namespace. + +## Deploy SFTPGo + +Commit the project: + +```bash +git add . +git commit -m "First deploy" +``` + +SFTPGo exposes its setup page publicly until the first administrator exists. Generate its password once, store it in your password manager, then create the account securely from environment variables during the first deployment: + +```bash +SFTPGO_ADMIN_USERNAME="admin" +SFTPGO_ADMIN_PASSWORD="$(openssl rand -base64 32)" + +clever env set SFTPGO_DEFAULT_ADMIN_USERNAME "$SFTPGO_ADMIN_USERNAME" +clever env set SFTPGO_DEFAULT_ADMIN_PASSWORD "$SFTPGO_ADMIN_PASSWORD" +clever env set SFTPGO_DATA_PROVIDER__CREATE_DEFAULT_ADMIN true + +# Prints the generated configuration +clever env +``` + +You can replace `admin` with any administrator username. + +Generate an Ed25519 SSH host key and store it in the application environment. The startup task restores the same key on every application instance and rebuild, keeping the SFTP server fingerprint stable: + +```bash +clever env set SFTPGO_SSH_HOST_KEY_B64 "$(openssl genpkey -algorithm ED25519 | openssl base64 -A)" +``` + +SFTPGo supports multiple [host keys](https://docs.sftpgo.com/2.7/config-file/#sshsftp-server), but one Ed25519 key provides a secure default for modern SFTP clients. Keep this private environment variable defined: removing or replacing it changes the server fingerprint after the next restart. Deploy the application: + +```bash +clever deploy +``` + +Once SFTPGo has started, the administrator remains stored in PostgreSQL. Remove the bootstrap variables from the application environment and restart the application so they are no longer available to the running process. Then open the web administration interface: + +```bash +clever env rm SFTPGO_DEFAULT_ADMIN_USERNAME +clever env rm SFTPGO_DEFAULT_ADMIN_PASSWORD +clever env rm SFTPGO_DATA_PROVIDER__CREATE_DEFAULT_ADMIN + +clever restart +clever open +``` + +## Use Cellar as a storage backend + +A new Cellar add-on does not contain a bucket. Create one with Cellar Explorer in the [Clever Cloud Console](https://console.clever-cloud.com/), or with an [S3-compatible client](/developers/doc/addons/cellar/#managing-your-buckets). + +### Configure Cellar through the SFTPGo web interface + +Create a user in the SFTPGo web administration interface and select **S3-compatible** as its storage provider: + +| Setting | Value | +| ------- | ----- | +| Bucket | Your Cellar bucket name | +| Home Dir | `/tmp/sftpgo` | +| Region | `default` | +| Access Key | The `CELLAR_ADDON_KEY_ID` value | +| Access Secret | The `CELLAR_ADDON_KEY_SECRET` value | +| Endpoint | `https://` followed by the `CELLAR_ADDON_HOST` value | +| Use path-style addressing | Enabled | + +SFTPGo requires a local home directory for temporary files when using S3-compatible storage. The startup task creates `/tmp/sftpgo` on every application instance. The open-source edition [uses unlinked local files for transfers to and from object storage](https://github.com/drakkan/sftpgo/discussions/1968), so a transfer can temporarily consume its full size on the instance disk. Account for the largest expected files and simultaneous transfers when choosing the run instance; completed files remain stored in Cellar, but in-progress transfers can fail if local disk is exhausted. + +Force path-style addressing to support every valid Cellar bucket name, including names containing dots. The linked Cellar add-on makes the other values available in the application environment variables in the Clever Cloud Console. + +### Configure Cellar through the REST API + +You can also create the SFTPGo user with the [SFTPGo REST API](https://docs.sftpgo.com/latest/rest-api/) without opening the web interface. This example requires `curl` and `jq`. Display the application's domains and choose the one you want to use for the web API: + +```bash +clever domain +``` + +Set a variable with this domain, then retrieve the linked Cellar add-on ID with Clever Tools. Set the bucket name and new SFTP username. OpenSSL generates the user's password once, so the following commands can reuse it: + +```bash +SFTPGO_URL="https://your-sftpgo-domain.example.com" +CELLAR_ADDON_ID="$( + clever service -F json --only-addons | + jq --exit-status --raw-output \ + 'first(.addons[] | select(.realId | startswith("cellar_")) | .id)' +)" +SFTPGO_BUCKET="your-cellar-bucket" +SFTPGO_USERNAME="myUser" +SFTPGO_PASSWORD="$(openssl rand -base64 32)" +``` + +You can replace `myUser` with any username. Store `SFTPGO_PASSWORD` in your password manager before unsetting it at the end of this procedure. + +Request an API token with the administrator credentials defined before the first deployment: + +```bash +SFTPGO_TOKEN="$( + curl --fail --silent --show-error \ + --user "${SFTPGO_ADMIN_USERNAME}:${SFTPGO_ADMIN_PASSWORD}" \ + "${SFTPGO_URL}/api/v2/token" | + jq --exit-status --raw-output '.access_token' +)" +``` + +The `shell` output format returns the linked Cellar add-on variables as export statements. Load them directly into the current shell, then use them to build the SFTPGo user configuration: + +```bash +source <(clever addon env "$CELLAR_ADDON_ID" -F shell) + +jq --null-input \ + --arg username "$SFTPGO_USERNAME" \ + --arg password "$SFTPGO_PASSWORD" \ + --arg bucket "$SFTPGO_BUCKET" \ + --arg access_key "$CELLAR_ADDON_KEY_ID" \ + --arg access_secret "$CELLAR_ADDON_KEY_SECRET" \ + --arg endpoint "https://$CELLAR_ADDON_HOST" \ + '{ + status: 1, + username: $username, + password: $password, + home_dir: "/tmp/sftpgo", + permissions: {"/": ["*"]}, + filesystem: { + provider: 1, + s3config: { + bucket: $bucket, + region: "default", + access_key: $access_key, + access_secret: {status: "Plain", payload: $access_secret}, + endpoint: $endpoint, + force_path_style: true + } + } + }' | curl --fail --silent --show-error \ + --request POST \ + --header "Authorization: Bearer ${SFTPGO_TOKEN}" \ + --header "Content-Type: application/json" \ + --data-binary @- \ + "${SFTPGO_URL}/api/v2/users" +``` + +Once the user exists, remove its password, the API token and the Cellar credentials from your shell: + +```bash +unset SFTPGO_ADMIN_USERNAME SFTPGO_ADMIN_PASSWORD SFTPGO_PASSWORD SFTPGO_TOKEN +unset CELLAR_ADDON_KEY_ID CELLAR_ADDON_KEY_SECRET CELLAR_ADDON_HOST +``` + +## Connect with an SFTP client + +Use any SFTP client with the following settings: + +- **Host**: your application's `.cleverapps.io` domain for the `cleverapps` redirection created above +- **Port**: the port returned by `clever tcp-redirs add` +- **Username and credentials**: those of the SFTPGo user you created, not the administrator account + +## Learn more + +{{< cards >}} + {{< card link="/developers/doc/applications/linux" title="Linux applications" subtitle="Deploy any application" icon="linux" >}} + {{< card link="/developers/doc/addons/cellar" title="Cellar object storage" subtitle="Store files with an S3 API" icon="database" >}} + {{< card link="/developers/doc/addons/fs-bucket" title="FS Buckets" subtitle="Store files with SFTP access" icon="fsbucket" >}} + {{< card link="https://docs.sftpgo.com/latest/" title="SFTPGo documentation" subtitle="Configure users and storage" icon="book-open" >}} +{{< /cards >}} From 7016394695c0557f08fca16ff8f8751dcc9fc567 Mon Sep 17 00:00:00 2001 From: miton18 Date: Tue, 25 Aug 2026 14:25:07 +0200 Subject: [PATCH 113/214] metrics(index): document the FS Bucket storage metric --- content/doc/metrics/_index.md | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/content/doc/metrics/_index.md b/content/doc/metrics/_index.md index 6e8d1a578..ded3915a6 100644 --- a/content/doc/metrics/_index.md +++ b/content/doc/metrics/_index.md @@ -201,6 +201,88 @@ under an organisation. '' '' @clevercloud/app_consumption ``` +## Add-on storage metrics + +### FS Bucket + +The storage used by each of your [FS Bucket](/doc/addons/fs-bucket) add-ons is recorded as the +`fsbucket.storage` class, with one data point per hour. The value is a size in **bytes**. +This metric belongs to the `addon-api-fsbucket` Warp 10 application, which is not included in the +read token shown in an application's **Metrics** tab. Generate a token that includes it with +[`clever curl`](/doc/cli/#curl): + +```bash +clever curl -X POST -H "Content-Type: application/json" \ + -d '{"applications": ["addon-api-fsbucket"], "ttl": "P5D"}' \ + https://api.clever-cloud.com/v4/stats/organisations//tokens/read +``` + +`ttl` controls how long the token remains valid, not how far back it can query. It is an +[ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) and cannot exceed `P20D`, +which represents a period of 20 days. The token is returned in the `token` field of the response. + +Save the following [WarpScript](https://www.warp10.io/doc/reference) as `fsbucket.mc2`, replacing +the token and organisation ID with your values. It retrieves the storage used by every bucket of +the organisation over the last 30 days: + +```txt +[ '' 'fsbucket.storage' { 'owner_id' '' } NOW 30 d ] FETCH +``` + +Send it to the Clever Cloud Warp 10 endpoint: + +```bash +curl --data-binary @fsbucket.mc2 \ + https://c2-warp10-clevercloud-customers.services.clever-cloud.com/api/v0/exec +``` + +The API returns a Geo Time Series for each bucket, with its owner, ID, datacenter and storage +server as labels. A simplified response follows this structure: + +```json +[ + [ + { + "c": "fsbucket.storage", + "l": { + ".app": "addon-api-fsbucket", + "owner_id": "", + "app_id": "", + "datacenter": "", + "server_id": "" + }, + "a": {}, + "v": [ + [1787677200000000, 1048576] + ] + } + ] +] +``` + +Each entry in `v` contains a timestamp in microseconds followed by the storage used in bytes. The +owner ID starts with `orga_`, or `user_` for a personal organisation. Bucket IDs start with +`bucket_`, or `app_` for buckets created before the naming change. Datacenter values are not +case-normalised: both `PAR` and `par` exist in the data. Prefer not to filter on that label, or +match it case-insensitively. + +To return the total storage used by every bucket, aggregated hourly, append these operations after +`FETCH` in `fsbucket.mc2`: + +```txt +// Group points into one-hour buckets and keep the last value in each bucket +[ SWAP bucketizer.last NOW 1 h 0 ] BUCKETIZE + +// Sum all bucket series at matching timestamps +[ SWAP [] reducer.sum ] REDUCE +``` + +To follow a single bucket, replace the labels in `fsbucket.mc2` with: + +```txt +{ 'owner_id' '' 'app_id' '' } +``` + ## Publish your own metrics We currently support two ways to push / collect your metrics: the `statsd` protocol and `prometheus`. From 871a5ca7bab006db8f99e6a23a733700758e5d17 Mon Sep 17 00:00:00 2001 From: miton18 Date: Tue, 25 Aug 2026 14:25:07 +0200 Subject: [PATCH 114/214] addons(fs-bucket): link to the storage metric --- content/doc/addons/fs-bucket.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/content/doc/addons/fs-bucket.md b/content/doc/addons/fs-bucket.md index e563c6282..7039c99ca 100644 --- a/content/doc/addons/fs-bucket.md +++ b/content/doc/addons/fs-bucket.md @@ -162,3 +162,12 @@ FS Buckets use Clever Cloud-managed SFTP credentials and do not support custom S The **Add-on information** tab of your FS Bucket add-on displays the information you need to connect to your bucket using FTP. + +## Monitoring your storage usage + +The size of each bucket is recorded hourly in Warp 10 as the `fsbucket.storage` metric, in bytes, +which lets you chart your storage consumption over time. +The read token shown in an application's **Metrics** tab does not include this metric. You need to +generate a dedicated token that includes the FS Bucket metrics scope. + +- [Generate the required token and query the FS Bucket storage metric](/doc/metrics#fs-bucket) From 596db32b80db00a981cb44645d3481a760f431ea Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 19 Mar 2026 17:39:43 +0100 Subject: [PATCH 115/214] shared(ruby): correct static file serving instructions --- shared/ruby.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/shared/ruby.md b/shared/ruby.md index 21283d4d8..c2c192a5a 100644 --- a/shared/ruby.md +++ b/shared/ruby.md @@ -87,23 +87,11 @@ production: ### Manage your static files and assets -Static files are configured with [environment variables](#setting-up-environment-variables-on-clever-cloud): +For a Rails application using the asset pipeline, add `assets:precompile` to [`CC_RAKEGOALS`](#configure-rake-goals) so that assets are built during deployment. -`STATIC_FILES_PATH`: should point to a directory where your static files are stored. +Set `STATIC_FILES_PATH=public` to serve the generated files through NGINX consistently across Rails versions. Leave `STATIC_URL_PREFIX` unset for a standard Rails application. NGINX then serves common static file extensions from `public` and forwards other requests to Rails. -`STATIC_URL_PREFIX`: the URL path under which you want to serve static files (for example `/public/`) - -**Note**: the path of your folder must be absolute regarding the root of your application. - -**Note**: setting the `STATIC_URL_PREFIX` to `/` will make the deployment to fail. - -If you use the asset pipeline, make sure to include the `assets:precompile` task in the `CC_RAKEGOALS` environment variable value. - -```bash -CC_RAKEGOALS="db:migrate, assets:precompile" -``` - -**Note**: if your project uses `webpacker`, make sure to enable the dedicated build instance option in the **Information** menu of your application in the Clever Cloud console because `webpacker` needs a lot a resources when starting. +For other Rack applications, `STATIC_FILES_PATH` specifies the static files directory relative to the application root. You can optionally set `STATIC_URL_PREFIX` to expose these files under a specific URL path, such as `/assets`. Setting it to `/` makes NGINX look for every requested path in the static files directory before forwarding missing files to the application. ### Using Active Storage and Cellar S3 From c33f4c93868a1ba7418aae1f7c0d792d0f98064b Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 19 Mar 2026 17:57:46 +0100 Subject: [PATCH 116/214] applications(java): add shared instructions for variables with dots --- content/doc/applications/java/java-gradle.md | 17 ++++---------- content/doc/applications/java/java-jar.md | 13 ++--------- content/doc/applications/java/java-maven.md | 6 +++-- content/doc/applications/java/java-war.md | 6 +++-- content/doc/applications/scala/_index.md | 2 +- .../applications/scala/play-framework-2.md | 7 +++--- shared/java-env-injection.md | 23 +++++++++++++++++++ 7 files changed, 42 insertions(+), 32 deletions(-) create mode 100644 shared/java-env-injection.md diff --git a/content/doc/applications/java/java-gradle.md b/content/doc/applications/java/java-gradle.md index df428f9af..34e147fa5 100644 --- a/content/doc/applications/java/java-gradle.md +++ b/content/doc/applications/java/java-gradle.md @@ -76,6 +76,10 @@ You can use the following properties: | Optional | **build → goal** | is the target you want to use to build your project | | Required | **deploy → goal** | the goal/target and options you want to execute to deploy/run you project | + {{% content "new-relic" %}} + +{{% content "java-env-injection" %}} + ## Custom run command If you need to run a custom command @@ -88,17 +92,6 @@ Example: CC_RUN_COMMAND="java -jar somefile.jar " ``` -### Environment injection - -Clever Cloud can inject environment variables that are defined in the -dashboard and by add-ons linked to your application. - -For Java applications, the environment is injected in the `System.getProperties()` object. - -So, to use a variable, you just need `System.getProperties().getProperty("MY_VARIABLE")`. - -For Groovy applications, just use the `System.getProperty("MY_VARIABLE")`. - ## The Gradle Wrapper Since Gradle can come in many versions, Clever Cloud automatically support the [Gradle Wrapper ↗](https://www.gradle.org/docs/current/userguide/gradle_wrapper.html). @@ -122,8 +115,6 @@ Just create and commit the `gradlew` file and the wrapper `jar` and `properties` {{< /filetree/folder >}} {{< /filetree/container >}} - {{% content "new-relic" %}} - {{% content "deploy-git" %}} {{% content "more-config" %}} diff --git a/content/doc/applications/java/java-jar.md b/content/doc/applications/java/java-jar.md index c672c6add..5dca6563c 100644 --- a/content/doc/applications/java/java-jar.md +++ b/content/doc/applications/java/java-jar.md @@ -112,6 +112,8 @@ You can use the following properties: {{% content "new-relic" %}} +{{% content "java-env-injection" %}} + ## Custom run command If you need to run a custom command (or just pass options to the program), @@ -124,17 +126,6 @@ Example: CC_RUN_COMMAND="java -jar somefile.jar " ``` -### Environment injection - -Clever Cloud can inject environment variables that are defined in the -dashboard and by add-ons linked to your application. - -For Java applications, the environment is injected in the `System.getProperties()` object. - -So, to use a variable, you just need `System.getProperties().getProperty("MY_VARIABLE")`. - -For Groovy applications, just use the `System.getProperty("MY_VARIABLE")`. - {{% content "deploy-git" %}} {{% content "link-addon" %}} diff --git a/content/doc/applications/java/java-maven.md b/content/doc/applications/java/java-maven.md index dc2ef5706..c70be884c 100644 --- a/content/doc/applications/java/java-maven.md +++ b/content/doc/applications/java/java-maven.md @@ -114,6 +114,10 @@ Or use the `CC_MAVEN_PROFILES` environment variable. Eg. `CC_MAVEN_PROFILES="prod"`. + {{% content "new-relic" %}} + +{{% content "java-env-injection" %}} + ## Custom run command If you need to run a custom command @@ -126,8 +130,6 @@ Example: CC_RUN_COMMAND="java -jar somefile.jar " ``` - {{% content "new-relic" %}} - {{% content "deploy-git" %}} {{% content "link-addon" %}} diff --git a/content/doc/applications/java/java-war.md b/content/doc/applications/java/java-war.md index c9f81e1fe..c6f7fe40f 100644 --- a/content/doc/applications/java/java-war.md +++ b/content/doc/applications/java/java-war.md @@ -123,6 +123,8 @@ That you can do, by setting the "build" field object in the `war.json` file: |Required | **type** |- The tool you want to use to build your app.
- Can be "maven", "gradle", "sbt" or "ant" | |Required | **goal** |- The goal you want the tool to execute.
- Basically, for maven, you want to put "package" in here. | + {{% content "new-relic" %}} + ## Available containers for war.json Here's the list of the configuration values for the "container" field in `war.json` (with **End Of Life** versions tagged as `EOL`): @@ -140,6 +142,8 @@ Here's the list of the configuration values for the "container" field in `war.js | WILDFLY27 | Use Wildfly servlet container 27.x (see ) | EOL | | WILDFLY33 | Use Wildfly servlet container 33.x (see ) | EOL | +{{% content "java-env-injection" %}} + {{% content "url_healthcheck" %}} {{% content "request-flow" %}} @@ -155,8 +159,6 @@ Example: CC_RUN_COMMAND="java -jar somefile.jar " ``` - {{% content "new-relic" %}} - {{% content "deploy-git" %}} {{% content "link-addon" %}} diff --git a/content/doc/applications/scala/_index.md b/content/doc/applications/scala/_index.md index e8dcabcd5..99bff2862 100644 --- a/content/doc/applications/scala/_index.md +++ b/content/doc/applications/scala/_index.md @@ -91,7 +91,7 @@ application.secret=${APPLICATION_SECRET} {{% content "env-injection" %}} -To access environment variables from your code, just get them from the environment with `System.getenv("MY_VARIABLE")`. Be aware that it can return null. +In Scala code, retrieve an environment variable with `System.getenv("MY_VARIABLE")`, it returns `null` when the variable is not defined. {{% content "deploy-git" %}} diff --git a/content/doc/applications/scala/play-framework-2.md b/content/doc/applications/scala/play-framework-2.md index fc9b0da89..bfadbb9de 100644 --- a/content/doc/applications/scala/play-framework-2.md +++ b/content/doc/applications/scala/play-framework-2.md @@ -70,10 +70,11 @@ For instance, if your Sbt project contains a `shared` and `play` module and you {{% content "new-relic" %}} - {{% content "env-injection" %}} +{{% content "env-injection" %}} -To access the environment variables from your code, you need to add `my.option=${MY_VARIABLE}` in your `application.conf` file, and then use the configuration item `my.option` in your application. e.g `%clevercloud.db.url="jdbc:mysql://"${MYSQL_ADDON_HOST}"/"${MYSQL_ADDON_DB}`. -You can also use the `System.getenv("MY_VARIABLE")` method. Be aware that it can return null. +### Access environment variables from application.conf + +To expose an environment variable through Play configuration, add `my.option=${?MY_VARIABLE}` to `application.conf`, then read the `my.option` configuration key from your application. You can also access it directly with `System.getenv("MY_VARIABLE")`, which returns `null` when the variable is not defined. {{% content "deploy-git" %}} diff --git a/shared/java-env-injection.md b/shared/java-env-injection.md new file mode 100644 index 000000000..eee454112 --- /dev/null +++ b/shared/java-env-injection.md @@ -0,0 +1,23 @@ +## Environment injection + +Clever Cloud exposes [environment variables defined for your application](#setting-up-environment-variables-on-clever-cloud) alongside those provided by linked add-ons. Read a standard variable such as `MY_VARIABLE` from the process environment with: + +```java +String value = System.getenv("MY_VARIABLE"); +``` + +### Java system properties + +By default, Clever Cloud also passes variables to the generated launch command as Java system properties using `-D` options. When this command runs your application in the same JVM, retrieve a property with `System.getProperty("MY_VARIABLE")`. + +Variable names containing a dot (`.`) are not exported to the process environment. The generated launch command only receives them as Java system properties. For example, if you define `my.app.config=production` for your application, Clever Cloud adds `-Dmy.app.config=production` to the generated launch command. + +A Maven or Gradle deployment goal can start the application in another JVM. In this case, forwarding system properties depends on the plugin or task configuration. When the property reaches the application JVM, retrieve its value with: + +```java +String value = System.getProperty("my.app.config"); // returns "production" +``` + +This mechanism can configure frameworks such as Spring Boot or Quarkus without modifying configuration files. Prefer standard environment variable names for portable application configuration. + +Setting [`CC_RUN_COMMAND`](#custom-run-command) replaces the generated launch command, so Clever Cloud no longer adds these `-D` options automatically. With a custom run command, use standard environment variable names and map them in your application configuration. From a04414dcc4990ec3c0c897a2b550d7b814705984 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 19 Mar 2026 19:34:20 +0100 Subject: [PATCH 117/214] applications(php): document private repository authentication --- content/doc/applications/frankenphp.md | 2 ++ content/doc/applications/php/composer.md | 2 ++ shared/private-composer-repositories.md | 34 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 shared/private-composer-repositories.md diff --git a/content/doc/applications/frankenphp.md b/content/doc/applications/frankenphp.md index f340037b3..73f5dbc4b 100644 --- a/content/doc/applications/frankenphp.md +++ b/content/doc/applications/frankenphp.md @@ -63,6 +63,8 @@ To install development dependencies, set the `CC_PHP_DEV_DEPENDENCIES` environme > [!TIP] Use a local Composer version > If you put a `composer.phar` file at the root of your project, it will be used to install dependencies. +{{% content "private-composer-repositories" %}} + ## Custom PHP configuration To load your own PHP configuration directives, put a `php.ini` file at the root of your project. FrankenPHP will automatically use it. diff --git a/content/doc/applications/php/composer.md b/content/doc/applications/php/composer.md index 635ef8940..dd557fe6c 100644 --- a/content/doc/applications/php/composer.md +++ b/content/doc/applications/php/composer.md @@ -77,6 +77,8 @@ configure an OAuth token in your Composer configuration file or in a separate fi You can find more documentation about Composer configuration in the [Composer schema reference](https://getcomposer.org/doc/04-schema.md). +{{% content "private-composer-repositories" %}} + ## Post-build hook example You use Artisan to manage your project and you want to execute *artisan migrate* before running your app. diff --git a/shared/private-composer-repositories.md b/shared/private-composer-repositories.md new file mode 100644 index 000000000..0611e5041 --- /dev/null +++ b/shared/private-composer-repositories.md @@ -0,0 +1,34 @@ +## Private Composer repositories + +Composer can authenticate with private package repositories through the `COMPOSER_AUTH` [environment variable](/doc/develop/env-variables/). Its value is a JSON object containing Composer authentication settings, such as HTTP Basic, OAuth or an access token. + +For example, to use [Private Packagist](https://packagist.com/), add its repository to your `composer.json`, replacing `ORGANISATION` with its short name: + +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://repo.packagist.com/ORGANISATION" + }, + { + "packagist.org": false + } + ] +} +``` + +Then define `COMPOSER_AUTH` for your application with the username and token provided by Private Packagist: + +```json +{ + "http-basic": { + "repo.packagist.com": { + "username": "PACKAGIST_USERNAME", + "password": "PACKAGIST_TOKEN" + } + } +} +``` + +Composer reads this variable automatically during `composer install`. Never commit credentials to your repository. If you use a local `auth.json` file, add it to `.gitignore`. See the [Composer authentication documentation](https://getcomposer.org/doc/articles/authentication-for-private-packages.md) for other providers and authentication methods. For a private Git repository accessed over SSH, configure a [private SSH key](/doc/reference/common-configuration/#private-ssh-key) instead. From 88aae0ff641a3a9a51607c298f592886c0765406 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 19 Mar 2026 17:26:08 +0100 Subject: [PATCH 118/214] applications(php): detail MEMORY_LIMIT behavior --- content/doc/applications/php/_index.md | 46 +++++++++++-------- .../reference-environment-variables.md | 3 +- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/content/doc/applications/php/_index.md b/content/doc/applications/php/_index.md index 6b701439d..6bc1eb3bc 100644 --- a/content/doc/applications/php/_index.md +++ b/content/doc/applications/php/_index.md @@ -125,26 +125,32 @@ Additionally, you can set PHP's time zone setting with `.user.ini`. For instance date.timezone=Europe/Paris ``` -#### Memory Limit - -When php-fpm spawns a worker it allocates a smaller part of the application's memory to the worker, here is the allocated memory for each flavor: - - | Flavor | Memory Limit | - |----------|--------------| - |Pico | 64M | - |Nano | 64M | - |XS | 128M | - |S | 256M | - |M | 384M | - |L | 512M | - |XL | 768M | - |2XL | 1024M | - |3XL | 1536M | - |4XL+ | 2048M | - -To change this limit you can define `MEMORY_LIMIT` [environment variable](/doc/reference/reference-environment-variables#php). - -If you define a limit exceeding the application memory it will use the default one. +#### Memory limit + +Clever Cloud configures PHP's [`memory_limit`](https://www.php.net/manual/en/ini.core.php#ini.memory-limit) directive, which limits how much memory each PHP script can allocate. For web requests, this limit applies independently within each PHP-FPM worker; memory is not reserved when workers start. Both the default limit and the maximum value accepted through `MEMORY_LIMIT` depend on the application's available memory, which excludes memory reserved for the system: + +| Flavor | Default `memory_limit` | Maximum `MEMORY_LIMIT` | +|--------|-----------------------:|-----------------------:| +| Nano | 91 MiB | 358 MiB | +| XS | 161 MiB | 859 MiB | +| S | 249 MiB | 1683 MiB | +| M | 406 MiB | 3579 MiB | +| L | 658 MiB | 7523 MiB | +| XL | 1051 MiB | 15496 MiB | +| 2XL | 1377 MiB | 23503 MiB | +| 3XL | 1666 MiB | 31525 MiB | + +To change the limit, set [`MEMORY_LIMIT`](/doc/reference/reference-environment-variables#php) to a positive integer interpreted in MiB, without a unit suffix. + +For example, to allow each PHP script to allocate up to 128 MiB: + +```bash +clever env set MEMORY_LIMIT 128 +``` + +If `MEMORY_LIMIT` exceeds the maximum value for the selected flavor, Clever Cloud keeps the automatically calculated limit and logs a warning during deployment. Clever Cloud also calculates the number of PHP-FPM workers automatically. + +On XS and larger flavors, setting [`CC_CONFIGURATION_PM_MAX_CHILDREN`](/doc/reference/reference-environment-variables#php) can lower the calculated memory limit. On Nano, the platform always uses six workers. If you also set a valid `MEMORY_LIMIT`, it takes precedence over the adjustment based on the number of workers. ## Frameworks and CMS diff --git a/content/doc/reference/reference-environment-variables.md b/content/doc/reference/reference-environment-variables.md index 98ca417b8..e913296de 100644 --- a/content/doc/reference/reference-environment-variables.md +++ b/content/doc/reference/reference-environment-variables.md @@ -278,6 +278,7 @@ Use Linux runtime with [Mise package manager](#install-tools-with-mise-package-m |-----------------------|------------------------------|--------------------------------| |`ALWAYS_POPULATE_RAW_POST_DATA` | | | |`CC_COMPOSER_VERSION` | Choose your composer version between `2` or `lts` | 2 | +|[`CC_CONFIGURATION_PM_MAX_CHILDREN`](/doc/applications/php/#memory-limit) | Set the number of PHP-FPM workers. On XS and larger flavors, increasing it can lower the automatically calculated memory limit. Nano always uses six workers | Depends on the scaler size | |[`CC_CGI_IMPLEMENTATION`](/doc/applications/php/apache/#change-the-fastcgi-module) | Choose the Apache FastCGI module between `fastcgi` and `proxy_fcgi` | proxy_fcgi | |`CC_HTTP_BASIC_AUTH` | Restrict HTTP access to your application. Example: `login:password`. You can define multiple credentials using additional `CC_HTTP_BASIC_AUTH_n` (where `n` is a number) environment variables. | | | `CC_APACHE_HEADERS_SIZE` | Set the maximum size of the headers in Apache, between `8` and `256`. Effective value depends on deployment region. [Ask for a dedicated load balancer](https://console.clever-cloud.com/ticket-center-choice) for a specific value | 8 | @@ -305,7 +306,7 @@ Use Linux runtime with [Mise package manager](#install-tools-with-mise-package-m |`HTTP_TIMEOUT` | Define a custom HTTP timeout | 180 | |`LDAPTLS_CACERT` | | | |`MAX_INPUT_VARS` | | | -|`MEMORY_LIMIT` | Change the default memory limit | | +|[`MEMORY_LIMIT`](/doc/applications/php/#memory-limit) | Set the maximum memory each PHP script can allocate, as a positive integer interpreted in MiB without a unit suffix | Depends on the scaler size | |[`SESSION_TYPE`](/doc/applications/php/sessions-emails/#use-materia-kv-or-redis-to-store-php-sessions "Use Materia KV or Redis to store PHP sessions") | Choose `redis` to use it as session store | | |`SOCKSIFY_EVERYTHING` | | | |`SQREEN_API_APP_NAME` | The name of your sqreen application. | | From 243ef26329f8dc56e1d7adae8947447ca969c763 Mon Sep 17 00:00:00 2001 From: Gervais Blaise Date: Fri, 9 Jan 2026 10:42:36 +0100 Subject: [PATCH 119/214] addons(cellar): document directory synchronization with s3cmd --- content/doc/addons/cellar.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/content/doc/addons/cellar.md b/content/doc/addons/cellar.md index 21747bb28..72630432c 100644 --- a/content/doc/addons/cellar.md +++ b/content/doc/addons/cellar.md @@ -138,6 +138,22 @@ This list isn't exhaustive. Feel free to [suggest other clients that you would l The file is then publicly available at `https://.cellar-c2.services.clever-cloud.com/image.jpg`. {{< /tab >}} + {{< tab name="Synchronize" >}} + You can upload a directory's contents and update only changed files with `sync`. First, preview the operations without changing any objects: + + ```bash + s3cmd sync --dry-run path/to/source/ s3://bucket-name/ + ``` + + Then synchronize the directory: + + ```bash + s3cmd sync path/to/source/ s3://bucket-name/ + ``` + + Add `--acl-public` to the second command to make the uploaded files publicly readable. + {{< /tab >}} + {{< tab name="List" >}} You can list the files in your bucket, you should see the `image.png` file: From bfc4e1f544f9f7b52036da4111be7e72c7554a06 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 00:38:06 +0200 Subject: [PATCH 120/214] refactor(instructions): centralize agent guidance in AGENTS.md --- .cursorrules | 1 - .github/copilot-instructions.md | 321 -------------------------------- AGENTS.md | 169 +++++++++++++++++ CLAUDE.md | 148 +-------------- 4 files changed, 170 insertions(+), 469 deletions(-) delete mode 120000 .cursorrules delete mode 100644 .github/copilot-instructions.md create mode 100644 AGENTS.md mode change 100644 => 120000 CLAUDE.md diff --git a/.cursorrules b/.cursorrules deleted file mode 120000 index 02dd13412..000000000 --- a/.cursorrules +++ /dev/null @@ -1 +0,0 @@ -.github/copilot-instructions.md \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index f8d541562..000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,321 +0,0 @@ -# AI Content Creation Rules for Clever Cloud Documentation - -This document provides comprehensive guidance for AI systems on creating high-quality content for Clever Cloud's documentation platform, including technical documentation, guides, and changelog entries. - -## Development Environment - -This is a Hugo-based documentation site using the Hextra theme. - -### Hugo Development Commands -- **Build and serve locally**: `hugo server` (site available at http://localhost:1313) -- **Build for production**: `hugo` (outputs to `public/developers/`) -- **Preview with drafts**: `hugo server --buildDrafts` -- **Generate new content**: - - `hugo new content guides/.md` - - `hugo new content/doc/administrate/.md` - - `hugo new content --kind applications doc/applications/.md` -- **Update CLI reference**: `./update-cli-reference.sh` - -### Project Structure -- **Content organization**: `/content/` contains all documentation: - - `doc/` - Main documentation (applications, addons, CLI, etc.) - - `guides/` - Framework-specific tutorials and guides - - `changelog/` - Platform updates and announcements - - `api/` - API documentation - - `postmortem/` - Incident reports -- **Shared content**: `/shared/` contains reusable content blocks -- **Data files**: `/data/` contains structured data (runtime versions, tooltips, etc.) -- **Static assets**: `/static/` for images, fonts, and other assets - -### Content Management System -- Uses Hugo front matter with fields: `type`, `weight`, `linkTitle`, `description`, `excludeSearch`, `aliases`, `draft`, `tags`, `authors` -- New pages have `draft: true` by default - change to `false` to publish -- Tooltips defined in `data/tooltips.toml` and auto-display on hover -- **Quality enforcement**: - - Markdown linting via markdownlint-cli2 (config: `.markdownlintignore`, `.markdownlint.jsonc`) - - Editorial checks via Vale.sh (config: `.vale.ini`) - - Vocabulary in `.github/styles/config/vocabularies/Doc/accept.txt` - -## Content Types Overview - -### 1. Technical Documentation (`/content/doc/`) -Formal reference material for platform features, APIs, and configurations. -- **Style**: Professional, precise, instructional -- **Structure**: Hierarchical with clear sections -- **Purpose**: Enable users to accomplish specific tasks - -### 2. Guides & Tutorials (`/content/guides/`) -Step-by-step instructions for implementing specific technologies or frameworks. -- **Style**: Educational, conversational but focused -- **Structure**: Progressive steps with examples -- **Purpose**: Guide users through complete implementation - -### 3. Changelog Entries (`/content/changelog/`) -Technical blog-style posts announcing platform updates and new features. -- **Style**: Engaging, informative, personality allowed -- **Structure**: Context → announcement → practical examples → links -- **Purpose**: Inform users about platform evolution - -## Writing Style Guidelines - -### Universal Rules (All Content Types) - -#### Language Standards -- Use **second person** ("you") to address readers directly -- Write in **active voice** - avoid passive constructions -- Use **British spelling** for "organisation" not "organization" -- **Avoid first-person pronouns**: I, me, my, we, us, our, let's -- Keep sentences **short and clear** (max 25 words when possible) -- **No jargon** - explain technical terms when first introduced - -#### Prohibited Phrases -- Placeholder phrases: "please note", "at this time", "it should be noted" -- Overconfident claims: "simply", "just", "easily", "quickly", "obviously" -- Time-dependent promises: "soon", "in the future", "coming next month" -- Over-politeness: excessive use of "please" - -#### Required Elements -- **Don't assume prior knowledge** - over-explain rather than under-explain -- Address the reader's **specific use case** and context -- Provide **concrete examples** with real commands, code, or configurations -- Include **relevant links** to related documentation - -### Documentation-Specific Rules - -#### Structure Requirements -```yaml ---- -type: docs -linkTitle: Short Title -title: Full Page Title -description: SEO-friendly description explaining what users will learn -aliases: -- /old/url/path ---- -``` - -#### Content Organization -- **Overview section** - Brief explanation of what the technology/feature is -- **Create/Setup section** - How to get started -- **Configure section** - Detailed configuration options -- **Advanced features** - Optional capabilities -- **Examples and references** - Practical implementations - -#### Technical Specifications -- Always specify **current versions** of software/tools -- Include **environment variables** with exact names and examples -- Provide **command-line examples** with proper syntax -- Prefer **GitHub-style callouts for important information**. They render in both GitHub and the documentation site: - ```markdown - > [!NOTE] - > Context about new features. - - > [!TIP] - > Helpful suggestions. - - > [!WARNING] - > Important considerations. - ``` -- Use the Hugo `{{< callout >}}` shortcode only when GitHub-style syntax can't provide the required rendering or behaviour -- Limit callouts to one or two per page - -### Guide-Specific Rules - -#### Engaging Introduction -- Start with **hero subtitle shortcode** explaining the framework/tool benefits -- Use **hextra shortcodes** for enhanced presentation: - ```markdown - {{< hextra/hero-subtitle >}} - Brief engaging description of what users will build - {{< /hextra/hero-subtitle >}} - ``` - -#### Step-by-Step Structure -- Use **steps shortcode** for sequential instructions: - ```markdown - {{% steps %}} - ### Step Title - Content and commands - {{% /steps %}} - ``` - -- Include **tab groups** for different package managers/approaches: - ```markdown - {{< tabs items="npm,yarn,pnpm" >}} - {{< tab >}}Content for npm{{< /tab >}} - {{< tab >}}Content for yarn{{< /tab >}} - {{< /tabs >}} - ``` - -#### Learning Resources -- End with **cards section** linking to related documentation: - ```markdown - {{< cards >}} - {{< card link="/path" title="Title" subtitle="Description" icon="icon-name" >}} - {{< /cards >}} - ``` - -### Changelog-Specific Rules - -#### Front Matter Format -```yaml ---- -title: Descriptive title about the update/feature -description: Brief one-line summary -date: YYYY-MM-DD -tags: - - relevant-product-tags -authors: - - name: Full Name - link: https://github.com/username - image: https://github.com/username.png?size=40 -excludeSearch: true ---- -``` - -#### Writing Style for Changelog -- **More personality allowed** - can be engaging and slightly conversational -- **Lead with impact** - explain why this matters to users -- **Provide context** - link to upstream releases, related changes -- **Include practical examples** - show users exactly how to use new features -- **Multiple entries per day acceptable** - don't batch unrelated updates - -#### Content Flow Pattern -1. **Context paragraph** - What changed and why it matters -2. **Technical details** - Versions, new features, improvements -3. **Implementation examples** - Concrete commands or configuration -4. **Related links** - Documentation, examples, community resources - -#### Command Examples in Changelog -Always show **complete command sequences**: -```bash -# Enable the feature -clever features enable operators - -# Check current version -clever keycloak version check yourKeycloakNameOrId - -# Update to latest -clever keycloak version update yourKeycloakNameOrId -``` - -## Technical Implementation Standards - -### Code Examples -- **Always complete and runnable** - no placeholder variables without explanation -- **Include setup context** - show what directory, prerequisites needed -- **Use realistic names** - avoid "foo", "bar", "example" -- **Show expected output** when helpful - -### Environment Variables -- **Exact variable names** with proper casing: `CC_NODE_BUILD_TOOL` -- **Show complete examples**: - ```bash - CC_WEBROOT="public" - CC_RUN_COMMAND="npm start" - ``` -- **Explain the impact** of each variable - -### File References -- **Absolute paths** when referencing project structure -- **Relative paths** when showing user actions: `./package.json` -- **Proper syntax highlighting** for code blocks - -## Content Quality Checklist - -### Before Publishing Documentation -- [ ] All commands tested and work as shown -- [ ] Environment variables verified with exact syntax -- [ ] Links point to correct, existing pages -- [ ] Examples use realistic project names and values -- [ ] No first-person pronouns (I, we, us, our) -- [ ] Short, clear sentences under 25 words -- [ ] GitHub-style callouts used for important information where needed - -### Before Publishing Changelog -- [ ] Clear benefit/impact stated upfront -- [ ] Version numbers and dates accurate -- [ ] Complete command examples provided -- [ ] Author information included with GitHub avatar -- [ ] Tags relevant to affected products -- [ ] Links to documentation and examples working - -### Before Publishing Guides -- [ ] Step-by-step flow tested end-to-end -- [ ] All code examples complete and functional -- [ ] Prerequisites clearly stated -- [ ] Expected outcomes explained -- [ ] Hextra shortcodes used appropriately -- [ ] Learning resources section included - -## Shared Content Usage - -### Including Reusable Blocks -- Use `{{% content "filename" %}}` for basic shared content -- Use `{{% content-raw "filename" %}}` for content containing shortcodes -- Available shared blocks in `/shared/` directory include common procedures - -### Creating New Shared Content -- Extract **commonly repeated information** into `/shared/filename.md` -- **No headings** in shared content (breaks table of contents) -- Focus on **procedural steps** rather than context - -## Maintenance and Updates - -### Keeping Content Current -- **Version numbers** should reflect current platform state -- **Screenshots and UI references** need regular updates -- **External links** should be verified periodically -- **Deprecated features** should be clearly marked - -### Community Integration -- **Link to community discussions** for new features -- **Reference GitHub examples** when available -- **Encourage feedback** on experimental features -- **Update based on user reports** and common issues - -## Deployment Configuration - -The site is configured for Clever Cloud hosting with the `static` runtime and these required environment variables: -- `CC_WEBROOT="public"` -- `CC_STATIC_AUTOBUILD_OUTDIR="public/developers"` -- `SERVER_ERROR_PAGE_404="developers/404.html"` -- Optional: `CC_HUGO_VERSION="0.161"` to specify Hugo version (example value) - -## Quality Assurance Requirements - -### Commit Message Convention -Use `section(page): commit message` for content updates. The section and page must identify the documentation area being changed: - -```text -addons(postgresql): document pg_partman support -applications(nodejs): clarify pnpm configuration -changelog(metabase): announce 0.63.14 security update -``` - -Use standard Conventional Commits for documentation structure, Hugo configuration or templates, deployment, CI, tooling, and dependency changes: - -```text -feat(hugo): add a shortcode for version tables -fix(ci): run Vale on shared content -refactor(layouts): simplify changelog rendering -chore(deps): update the Hextra theme -``` - -Split content and structural changes into separate commits when possible. - -### Build Verification -Always test changes with the `hugo` command before committing to ensure the build is functional. Fix any build errors immediately as they prevent deployment. Verify that all links, references, image paths, and shortcode syntax work correctly in the generated output. - -### File Standards -All files must end with a single blank line to comply with git standards and POSIX requirements. - -### Content Quality Standards -Minimise the use of bullet points and lists. Use them only when essential for clarity or information efficiency. -Structure each section with 2-4 well-developed paragraphs. Avoid single text blocks or overly fragmented content. -Aim for paragraphs of 3-6 lines for optimal readability. Each paragraph should contain a complete thought or concept. -Use descriptive section titles. Titles should create useful table of contents entries. -Organise information logically so each section forms a coherent, useful unit. -Ensure all necessary information is included in readable prose, not fragmented lists. - -This document ensures consistent, high-quality content across all Clever Cloud documentation while respecting the different styles appropriate for technical docs, educational guides, and announcement posts. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..b56ac8c4c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,169 @@ +# AGENTS.md + +This file provides repository-wide guidance to coding agents working on Clever Cloud documentation. Task-specific deployment-guide workflows live in `.agents/skills/`. + +## Common Development Commands + +### Hugo Site Development +- **Local development**: `hugo server` - Serves site at http://localhost:1313 with live reload +- **Build for production**: `hugo` - Outputs to `public/developers/` +- **Preview drafts**: `hugo server --buildDrafts` - Include draft content in local preview +- **Update CLI reference**: `./update-cli-reference.sh` - Fetches latest clever-tools documentation + +### Content Generation +- **New guide**: `hugo new content guides/.md` +- **New documentation**: `hugo new content/doc/administrate/.md` +- **New application runtime**: `hugo new content --kind applications doc/applications/.md` + +## Project Architecture + +### Content Organization +This is a Hugo-based documentation site using the Hextra theme with the following structure: + +- **`/content/`** - All documentation content: + - `doc/` - Main technical documentation (applications, addons, CLI, administration) + - `guides/` - Framework-specific tutorials and step-by-step guides + - `changelog/` - Platform updates and feature announcements + - `api/` - API reference documentation + - `postmortem/` - Incident reports and analysis +- **`/shared/`** - Reusable content blocks included via `{{% content "filename" %}}` shortcode +- **`/data/`** - Structured data files: + - `runtime_versions.yml` - Supported runtime versions and EOL status + - `tooltips.toml` - Tooltip definitions that auto-display on hover + - `icons.yaml`, `software_versions_shared_dedicated.yml` - Additional data +- **`/static/`** - Static assets (images, fonts, favicon, etc.) +- **`/layouts/`** - Hugo templates and shortcodes for content rendering + +### Content Types and Front Matter +Content uses Hugo front matter with fields such as: +- `type: docs` - Content layout type +- `weight` - Sidebar ordering (integer) +- `linkTitle` - Short title for sidebar navigation +- `description` - SEO meta description +- `keywords` - Array of SEO keywords +- `aliases` - Redirect paths for pages that previously existed at another URL; don't add aliases to a new page +- `draft: true` - Prevents publishing; remove this field when the page is ready instead of keeping `draft: false` +- `excludeSearch: true` - Excludes from search index (recommended for changelog) + +For changelog entries, also include: +- `date: YYYY-MM-DD` - Publication date +- `tags` - Array of product tags (lowercase) +- `authors` - Array with `name`, `link`, `image` fields + +### Shared Content System +- Include shared content: `{{% content "filename" %}}` +- Include shared content with shortcodes: `{{% content-raw "filename" %}}` +- Shared files should not contain headings (breaks ToC generation) + +## Quality Standards + +### Content Quality Requirements +- Use second person ("you") addressing readers directly +- Write in active voice, avoid passive constructions +- Use `organisation` and `organisations`, rather than `organization` and `organizations`; this isn't a general British-spelling requirement +- Keep sentences under 25 words when possible +- Provide concrete examples with real commands and configurations +- Explain prerequisites and non-obvious behaviour, while keeping the main task path concise + +### Prohibited Elements +- First-person pronouns: I, me, my, we, us, our, let's +- Placeholder phrases: "please note", "at this time", "it should be noted" +- Overconfident claims: "simply", "just", "easily", "quickly", "obviously" +- Time-dependent promises: "soon", "in the future", "coming next month" + +### Markdown and Editorial Standards +- **Markdown linting**: Uses markdownlint-cli2 with config in `.markdownlint.jsonc` +- **Editorial checks**: Uses Vale.sh for style and terminology +- **Build verification**: Always test with `hugo` command before committing +- **Structure**: Use 2-4 well-developed paragraphs per section, minimize bullet lists +- **Paragraphs**: Aim for 3-6 lines for optimal readability + +### Callouts +- Prefer GitHub-style callouts with a concise title on the marker line, as supported by the theme: + ```markdown + > [!NOTE] Current behaviour + > This information helps readers understand the current behaviour + + > [!WARNING] Back up your data + > Back up your application database before upgrading + ``` +- Use the Hugo `{{< callout >}}` shortcode only when GitHub-style syntax can't provide the required rendering or behaviour +- Limit callouts to one or two per page + +### Commit Messages +- For content updates, use `section(page): commit message`, for example: + - `addons(postgresql): document pg_partman support` + - `applications(nodejs): clarify pnpm configuration` + - `changelog(metabase): announce 0.63.14 security update` + - `guides: add SvelteKit` for a new deployment guide +- For documentation structure, Hugo, deployment, CI, tooling, or dependency changes, use standard Conventional Commits, for example: + - `feat(hugo): add a shortcode for version tables` + - `fix(ci): run Vale on shared content` + - `refactor(layouts): simplify changelog rendering` + - `chore(deps): update the Hextra theme` +- Split content and structural changes into separate commits when possible +- Start the subject with a lowercase imperative verb + +### Code and Technical Examples +- Always provide complete, runnable code examples +- Keep commands literally copyable: don't put shell-invalid placeholders or bracketed optional arguments in executable code blocks +- Show optional flags in separate examples or explain where to add them +- Use exact environment variable names: `CC_WEBROOT`, `CC_NODE_BUILD_TOOL`, etc. +- Include setup context and expected output when helpful +- Use realistic names instead of "foo", "bar", "example" +- Show complete command sequences in changelog entries +- Add code comments only when they explain non-obvious behaviour +- Don't add a full stop to a short standalone line made of one simple sentence, especially a single-line code comment, label, or concise list item. Use normal terminal punctuation for developed or multi-sentence prose, including list items. +- Don't hard-wrap prose with formatting-only line breaks that don't affect rendering; keep each paragraph on one logical line and rely on editor word wrap. +- Sort lists and tables alphabetically unless a functional or chronological order is more useful + +### Deployment Guides + +- Prefer one clear path that uses platform defaults and native features +- Follow every documented command from a fresh project and test every behaviour the guide promises +- Verify the default build and run configuration before documenting any scaling override +- Omit `-a` when the current directory is linked to only one application; use an application alias to disambiguate or link resources +- Follow the option style used by recent guides, including short Clever Tools options where established +- Place the standard `clever domain` and custom-domain example immediately after application creation +- Use `openssl rand -base64 32` when a guide needs a portable password-generation example and OpenSSL is already a prerequisite +- Make sure captured command output doesn't expose credentials, tokens, add-on environment values, or unrelated account data +- Validate public-storage claims with an unauthenticated request and persistence claims after a restart or rebuild +- Inspect generated HTML for tabs, code blocks, cards, links, and copyable commands; a successful Hugo build alone doesn't validate rendering +- Give `title` a descriptive, SEO-oriented value without repeating "Clever Cloud"; keep `linkTitle` short, usually the product name + +## Deployment Configuration +The site is configured for Clever Cloud hosting with the `static` runtime and these required environment variables: +- `CC_WEBROOT="public"` +- `CC_STATIC_AUTOBUILD_OUTDIR="public/developers"` +- `SERVER_ERROR_PAGE_404="developers/404.html"` +- Optional: `CC_HUGO_VERSION="0.161"` to specify Hugo version (example value) + +## Data Management +Runtime versions and software compatibility information is maintained in `/data/runtime_versions.yml` and should be kept current with platform capabilities. The site generates various output formats including standard HTML and a special LLMS output format at `/llms.txt` for AI consumption. + +## Hugo Shortcodes and Features + +### Content Shortcodes +- `{{% content "filename" %}}` - Include shared content from `/shared/` directory +- `{{% content-raw "filename" %}}` - Include shared content containing shortcodes +- `{{% steps %}}` - Create step-by-step instructions for guides +- `{{< tabs >}}` with named `{{< tab name="npm" >}}` children - Create tabbed content sections +- `{{< cards >}}` - Display card layouts for related resources +- `{{< callout >}}` - Create a callout only when GitHub-style syntax isn't sufficient +- `{{< hextra/hero-subtitle >}}` - Add engaging subtitles in guides + +### Hugo Content Types +- **Documentation pages**: Use `type: docs` in front matter +- **Guides**: Use step-by-step structure, hero subtitles, and cards when they improve the guide +- **Changelog entries**: Include date, tags, and author information +- **API documentation**: Structured reference content + +### Hextra Theme Features +- **Search**: Full-text search using FlexSearch +- **Dark mode**: Automatic theme switching +- **Responsive navigation**: Sidebar and mobile-friendly menus +- **Edit links**: Direct GitHub editing integration +- **Syntax highlighting**: Code block highlighting with copy functionality + +## File Standards +All text files must end with a newline. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 063018b21..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,147 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. Additional AI guidance is available in `.github/copilot-instructions.md` for comprehensive content creation rules. - -## Common Development Commands - -### Hugo Site Development -- **Local development**: `hugo server` - Serves site at http://localhost:1313 with live reload -- **Build for production**: `hugo` - Outputs to `public/developers/` -- **Preview drafts**: `hugo server --buildDrafts` - Include draft content in local preview -- **Update CLI reference**: `./update-cli-reference.sh` - Fetches latest clever-tools documentation - -### Content Generation -- **New guide**: `hugo new content guides/.md` -- **New documentation**: `hugo new content/doc/administrate/.md` -- **New application runtime**: `hugo new content --kind applications doc/applications/.md` - -## Project Architecture - -### Content Organization -This is a Hugo-based documentation site using the Hextra theme with the following structure: - -- **`/content/`** - All documentation content: - - `doc/` - Main technical documentation (applications, addons, CLI, administration) - - `guides/` - Framework-specific tutorials and step-by-step guides - - `changelog/` - Platform updates and feature announcements - - `api/` - API reference documentation - - `postmortem/` - Incident reports and analysis -- **`/shared/`** - Reusable content blocks included via `{{% content "filename" %}}` shortcode -- **`/data/`** - Structured data files: - - `runtime_versions.yml` - Supported runtime versions and EOL status - - `tooltips.toml` - Tooltip definitions that auto-display on hover - - `icons.yaml`, `software_versions_shared_dedicated.yml` - Additional data -- **`/static/`** - Static assets (images, fonts, favicon, etc.) -- **`/layouts/`** - Hugo templates and shortcodes for content rendering - -### Content Types and Front Matter -All content uses Hugo front matter with specific fields: -- `type: docs` - Content layout type -- `weight` - Sidebar ordering (integer) -- `linkTitle` - Short title for sidebar navigation -- `description` - SEO meta description -- `keywords` - Array of SEO keywords -- `aliases` - Array of redirect paths (relative to base URL) -- `draft: true` - Prevents publishing (change to `false` to publish) -- `excludeSearch: true` - Excludes from search index (recommended for changelog) - -For changelog entries, also include: -- `date: YYYY-MM-DD` - Publication date -- `tags` - Array of product tags (lowercase) -- `authors` - Array with `name`, `link`, `image` fields - -### Shared Content System -- Include shared content: `{{% content "filename" %}}` -- Include shared content with shortcodes: `{{% content-raw "filename" %}}` -- Shared files should not contain headings (breaks ToC generation) - -## Quality Standards - -### Content Quality Requirements -- Use second person ("you") addressing readers directly -- Write in active voice, avoid passive constructions -- Use British spelling: "organisation" not "organization" -- Keep sentences under 25 words when possible -- Provide concrete examples with real commands and configurations -- Over-explain rather than under-explain technical concepts - -### Prohibited Elements -- First-person pronouns: I, me, my, we, us, our, let's -- Placeholder phrases: "please note", "at this time", "it should be noted" -- Overconfident claims: "simply", "just", "easily", "quickly", "obviously" -- Time-dependent promises: "soon", "in the future", "coming next month" - -### Markdown and Editorial Standards -- **Markdown linting**: Uses markdownlint-cli2 with config in `.markdownlint.jsonc` -- **Editorial checks**: Uses Vale.sh for style and terminology -- **Build verification**: Always test with `hugo` command before committing -- **Structure**: Use 2-4 well-developed paragraphs per section, minimize bullet lists -- **Paragraphs**: Aim for 3-6 lines for optimal readability - -### Callouts -- Prefer GitHub-style callouts because they render in both GitHub and the documentation site: - ```markdown - > [!NOTE] - > This information helps readers understand the current behaviour. - - > [!WARNING] - > Back up your application database before upgrading. - ``` -- Use the Hugo `{{< callout >}}` shortcode only when GitHub-style syntax can't provide the required rendering or behaviour -- Limit callouts to one or two per page - -### Commit Messages -- For content updates, use `section(page): commit message`, for example: - - `addons(postgresql): document pg_partman support` - - `applications(nodejs): clarify pnpm configuration` - - `changelog(metabase): announce 0.63.14 security update` -- For documentation structure, Hugo, deployment, CI, tooling, or dependency changes, use standard Conventional Commits, for example: - - `feat(hugo): add a shortcode for version tables` - - `fix(ci): run Vale on shared content` - - `refactor(layouts): simplify changelog rendering` - - `chore(deps): update the Hextra theme` -- Split content and structural changes into separate commits when possible - -### Code and Technical Examples -- Always provide complete, runnable code examples -- Use exact environment variable names: `CC_WEBROOT`, `CC_NODE_BUILD_TOOL`, etc. -- Include setup context and expected output when helpful -- Use realistic names instead of "foo", "bar", "example" -- Show complete command sequences in changelog entries - -## Deployment Configuration -The site is configured for Clever Cloud hosting with the `static` runtime and these required environment variables: -- `CC_WEBROOT="public"` -- `CC_STATIC_AUTOBUILD_OUTDIR="public/developers"` -- `SERVER_ERROR_PAGE_404="developers/404.html"` -- Optional: `CC_HUGO_VERSION="0.161"` to specify Hugo version (example value) - -## Data Management -Runtime versions and software compatibility information is maintained in `/data/runtime_versions.yml` and should be kept current with platform capabilities. The site generates various output formats including standard HTML and a special LLMS output format at `/llms.txt` for AI consumption. - -## Hugo Shortcodes and Features - -### Content Shortcodes -- `{{% content "filename" %}}` - Include shared content from `/shared/` directory -- `{{% content-raw "filename" %}}` - Include shared content containing shortcodes -- `{{% steps %}}` - Create step-by-step instructions for guides -- `{{< tabs items="npm,yarn,pnpm" >}}` - Create tabbed content sections -- `{{< cards >}}` - Display card layouts for related resources -- `{{< callout >}}` - Create a callout only when GitHub-style syntax isn't sufficient -- `{{< hextra/hero-subtitle >}}` - Add engaging subtitles in guides - -### Hugo Content Types -- **Documentation pages**: Use `type: docs` in front matter -- **Guides**: Use step-by-step structure with hero subtitles and cards -- **Changelog entries**: Include date, tags, and author information -- **API documentation**: Structured reference content - -### Hextra Theme Features -- **Search**: Full-text search using FlexSearch -- **Dark mode**: Automatic theme switching -- **Responsive navigation**: Sidebar and mobile-friendly menus -- **Edit links**: Direct GitHub editing integration -- **Syntax highlighting**: Code block highlighting with copy functionality - -## File Standards -All files must end with a single blank line to comply with git standards and POSIX requirements. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 000000000..47dc3e3d8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file From 99e22749a628ef366ba42af8f7d50369a8fae7a4 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 00:40:38 +0200 Subject: [PATCH 121/214] docs: clarify formatting, validation and guide commit conventions --- CONTRIBUTING.md | 54 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ddd345641..d1952ed81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,13 @@ # Contributing guidelines -This document provides explicit standards expected in Clever Cloud documentation. Inspiration and research for this document comes from the incredible [Awesome Code Review](https://github.com/joho/awesome-code-review) project. You'll find instructions for AI tools and LLMs: +This document provides explicit standards expected in Clever Cloud documentation. Inspiration and research for this document comes from the incredible [Awesome Code Review](https://github.com/joho/awesome-code-review) project. You'll find repository instructions for coding agents and LLMs: -- [AI tools and LLMs instructions](./.cursorrules) +- [Coding agent instructions](./AGENTS.md) ## Standards -Those are general standards to fulfill for every modification in this repository. +Those are general standards to fulfill for every modification in this repository ### 🏯 Structure @@ -23,42 +23,53 @@ Follow the established structure in this doc. If you wish to propose changes to Follow these guidelines while writing new content. The goal is to help you write in a **clear, precise, and unambiguous language**. They're not meant to be a burden, but to help you deliver the best content possible. -Sources for quality content are currently being updated. +Sources for quality content are currently being updated #### 👍 Do -- **Don't assume the user "knows better":** if you think something is obvious, it's not. Better over-explain than under-explain. -- **Use active voice:** passive voice can make it harder for the readers to figure out who's supposed to do something. -- **Use second person:** address the reader directly. -- **Keep it simple:** avoid jargon, complex sentences, and jokes. +- **Don't assume prior knowledge:** explain prerequisites and non-obvious behaviour without burying the main task path +- **Use active voice:** passive voice can make it harder for the readers to figure out who's supposed to do something +- **Use second person:** address the reader directly +- **Keep it simple:** avoid jargon, complex sentences, and jokes - **Keep it short:** keep the sentences short. Titles should be short and to the point. Keep longer content for the description metadata or the card subtitle. #### 👎 Don't -- Placeholder phrases like _please note_ and _at this time_. -- Words and phrases that make promises or project plans and strategies: See [Writing timeless documentation](https://developers.google.com/style/timeless-documentation). -- Using phrases like _simply_, _It's that simple_, _It's easy_, or _quickly_ in a procedure. -- Over-politeness with the use of _please_: go straight to the point. +- Placeholder phrases like _please note_ and _at this time_ +- Words and phrases that make promises or project plans and strategies: See [Writing timeless documentation](https://developers.google.com/style/timeless-documentation) +- Using phrases like _simply_, _It's that simple_, _It's easy_, or _quickly_ in a procedure +- Over-politeness with the use of _please_: go straight to the point #### 💡 Shortcodes and callouts This doc uses Hugo with [Hextra theme](https://imfing.github.io/hextra/), which provides a variety of [shortcodes](https://imfing.github.io/hextra/docs/guide/shortcodes/) to enhance it and improve its readability. For example : -- [Steps](https://imfing.github.io/hextra/docs/guide/shortcodes/steps/) are well suited for the `/guides/` section, or for any tutorial. +- [Steps](https://imfing.github.io/hextra/docs/guide/shortcodes/steps/) are well suited for the `/guides/` section, or for any tutorial -Use GitHub-style callouts whenever possible. They remain readable on GitHub and don't depend on Hugo-specific rendering: +Use GitHub-style callouts with a concise title on the marker line, as supported by the theme: ```markdown -> [!NOTE] -> This information helps readers understand the current behaviour. +> [!NOTE] Current behaviour +> This information helps readers understand the current behaviour -> [!WARNING] -> Back up your application database before upgrading. +> [!WARNING] Back up your data +> Back up your application database before upgrading ``` Use the [Hextra callout shortcode](https://imfing.github.io/hextra/docs/guide/shortcodes/callout/) only when a GitHub-style callout can't provide the required rendering or behaviour. Don't overuse callouts: limit them to one or two per page. +#### Front matter and examples + +- Give `title` a descriptive, SEO-oriented value without repeating "Clever Cloud", which the generated HTML title already includes. Keep `linkTitle` short, usually the product name, so it fits in the sidebar. +- Don't add `aliases` to a new page. Use them only to preserve URLs that existed previously. +- Remove `draft: true` when a page is ready to publish instead of keeping `draft: false` +- Keep commands literally copyable. Don't use shell-invalid placeholders or bracketed optional arguments in executable code blocks; show optional variants separately or explain where to add the flag. +- Add code comments only when they explain non-obvious behaviour +- Don't add a full stop to a short standalone line made of one simple sentence, especially a single-line code comment, label, or concise list item. Use normal terminal punctuation for developed or multi-sentence prose, including list items. +- Don't hard-wrap prose with formatting-only line breaks that don't affect rendering. Keep each paragraph on one logical line and rely on editor word wrap. +- Sort lists and tables alphabetically unless a functional or chronological order is more useful + ### 💅 Style guide - Don't override global styles for font type, size, or color @@ -66,6 +77,10 @@ Use the [Hextra callout shortcode](https://imfing.github.io/hextra/docs/guide/sh - When importing from an external CSS tool, import the relevant classes only rather than the whole file - Opt for self hosting over CDN: When used in ``, it can impact site's performance. Using CDN for test purposes when submitting your PR and deploying a review app is totally fine, however. +### ✅ Validation + +Run `hugo` before submitting a change and fix any build error. Verify links, references, image paths, and shortcode syntax in the generated output. Inspect changed tabs, code blocks, cards, anchors, and copyable commands in the rendered HTML: a successful build alone doesn't prove that they render correctly. + ## 🫶 Pull requests These are the guidelines when submitting or reviewing a PR in this repository. The better you follow them, the faster the is the review process. @@ -87,8 +102,11 @@ For content updates, use `section(page): commit message`. The section and page i addons(postgresql): document pg_partman support applications(nodejs): clarify pnpm configuration changelog(metabase): announce 0.63.14 security update +guides: add SvelteKit ``` +Use `guides: add Product` when adding a deployment guide. Start the subject with a lowercase imperative verb. + For changes to the documentation structure, Hugo configuration or templates, deployment, CI, tooling, or dependencies, use the standard Conventional Commits format `type(scope): commit message`: ```text From 607065169b6b662c99eff9b0e8d702714e1d601a Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 01:03:58 +0200 Subject: [PATCH 122/214] guides: improve page titles --- content/guides/_index.md | 2 +- content/guides/astro.md | 2 +- content/guides/docs.md | 2 +- content/guides/docusaurus.md | 2 +- content/guides/ekg-statsd-haskell-metrics.md | 2 +- content/guides/eleventy.md | 2 +- content/guides/fluentd.md | 2 +- content/guides/go-echoip.md | 5 ++--- content/guides/hexo.md | 2 +- content/guides/hono.md | 2 +- content/guides/hugo-static-s3.md | 3 +-- content/guides/hugo.md | 2 +- content/guides/kibana.md | 2 +- content/guides/lume-deno.md | 2 +- content/guides/mdbook.md | 2 +- content/guides/mkdocs.md | 2 +- content/guides/moodle.md | 2 +- content/guides/node-js-mongo-db.md | 2 +- content/guides/node-statsd-nodejs-metrics.md | 2 +- content/guides/nuxt.md | 2 +- content/guides/otree.md | 3 +-- content/guides/pgpool.md | 2 +- content/guides/proxysql.md | 2 +- content/guides/python-django-sample.md | 2 +- content/guides/ruby-on-rails.md | 2 +- content/guides/ruby-rack-app-tutorial.md | 2 +- content/guides/ruby-rack.md | 2 +- content/guides/sveltekit.md | 2 +- content/guides/tutorial-drupal.md | 2 +- content/guides/tutorial-laravel.md | 2 +- content/guides/tutorial-symfony.md | 2 +- content/guides/tutorial-wordpress.md | 2 +- 32 files changed, 33 insertions(+), 36 deletions(-) diff --git a/content/guides/_index.md b/content/guides/_index.md index a4be1e7e1..02f7d1813 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Guides -title: Guides +title: Deployment guides description: Step-by-step guides to deploy specific apps and frameworks on Clever Cloud with detailed tutorials and configuration examples keywords: - guides diff --git a/content/guides/astro.md b/content/guides/astro.md index d7594934c..87e929579 100644 --- a/content/guides/astro.md +++ b/content/guides/astro.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Astro -title: Astro +title: Deploy an Astro application description: Deploy Astro as Static Site Generator (SSG) or with Node.js on Clever Cloud with step-by-step tutorial and configuration guide keywords: - astro diff --git a/content/guides/docs.md b/content/guides/docs.md index 873575a7b..bdf7cbb58 100644 --- a/content/guides/docs.md +++ b/content/guides/docs.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Docs -title: Docs +title: Deploy the Docs collaborative editor description: Deploy the open source collaborative document editor Docs on Clever Cloud with Python backend and Node.js frontend keywords: - docs diff --git a/content/guides/docusaurus.md b/content/guides/docusaurus.md index 1aa0580d9..5b7f00be2 100644 --- a/content/guides/docusaurus.md +++ b/content/guides/docusaurus.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Docusaurus -title: Docusaurus +title: Deploy a Docusaurus website description: Deploy Docusaurus Static Site Generator (SSG) websites on Clever Cloud with step-by-step tutorial and configuration guide keywords: - docusaurus diff --git a/content/guides/ekg-statsd-haskell-metrics.md b/content/guides/ekg-statsd-haskell-metrics.md index 29cd88d9a..476207a05 100644 --- a/content/guides/ekg-statsd-haskell-metrics.md +++ b/content/guides/ekg-statsd-haskell-metrics.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Ekg-statsd -title: Ekg-statsd +title: Export Haskell metrics with ekg-statsd description: Configure ekg-statsd package on your Haskell application to push custom metrics with detailed step-by-step tutorials keywords: - haskell diff --git a/content/guides/eleventy.md b/content/guides/eleventy.md index 7adbef167..38e874910 100644 --- a/content/guides/eleventy.md +++ b/content/guides/eleventy.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Eleventy (11ty) -title: Eleventy (11ty) +title: Deploy an Eleventy website description: Deploy Eleventy (11ty) Static Site Generator (SSG) websites on Clever Cloud with step-by-step tutorial and configuration guide keywords: - eleventy diff --git a/content/guides/fluentd.md b/content/guides/fluentd.md index 9f79bd254..b846365df 100644 --- a/content/guides/fluentd.md +++ b/content/guides/fluentd.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Fluentd -title: Fluentd +title: Deploy Fluentd with Docker description: Deploy Fluentd log collector using Docker on Clever Cloud with detailed step-by-step tutorials and configuration examples keywords: - fluentd diff --git a/content/guides/go-echoip.md b/content/guides/go-echoip.md index fab10e8ff..18dc402ed 100644 --- a/content/guides/go-echoip.md +++ b/content/guides/go-echoip.md @@ -1,7 +1,7 @@ --- type: docs -linkTitle: Go - Echoip -title: Go - Echoip +linkTitle: EchoIP +title: Deploy EchoIP with Go description: Build and deploy a Go EchoIP service on Clever Cloud with step-by-step instructions, configuration examples, and optimization tips keywords: - go @@ -78,4 +78,3 @@ Check the deployment logs: ``` echoip/ ~ $ clever logs ``` - diff --git a/content/guides/hexo.md b/content/guides/hexo.md index f0d5779bb..733f7df51 100644 --- a/content/guides/hexo.md +++ b/content/guides/hexo.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Hexo -title: Hexo +title: Deploy a Hexo website description: Deploy Hexo Static Site Generator (SSG) websites on Clever Cloud with step-by-step tutorial and configuration guide keywords: - hexo diff --git a/content/guides/hono.md b/content/guides/hono.md index dc0b3e5a6..cbb46bb2c 100644 --- a/content/guides/hono.md +++ b/content/guides/hono.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Hono -title: Hono +title: Deploy a Hono application description: Deploy a Hono application on Clever Cloud with the Node.js runtime and a step-by-step configuration guide keywords: - hono diff --git a/content/guides/hugo-static-s3.md b/content/guides/hugo-static-s3.md index b67491610..9704809f8 100644 --- a/content/guides/hugo-static-s3.md +++ b/content/guides/hugo-static-s3.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Hugo + Cellar -title: Hugo + Cellar +title: Host a Hugo website with Cellar description: Build your website with Hugo and deploy it through Cellar S3-compatible object storage with detailed step-by-step tutorials keywords: - hugo @@ -170,4 +170,3 @@ If you still encounter an issue at this point, please contact our support team. - diff --git a/content/guides/hugo.md b/content/guides/hugo.md index 83bed3f47..ec655a365 100644 --- a/content/guides/hugo.md +++ b/content/guides/hugo.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Hugo -title: Hugo +title: Deploy a Hugo website description: Deploy Hugo Static Site Generator (SSG) websites on Clever Cloud with step-by-step tutorial and configuration guide keywords: - hugo diff --git a/content/guides/kibana.md b/content/guides/kibana.md index 431449859..6368fdacc 100644 --- a/content/guides/kibana.md +++ b/content/guides/kibana.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Kibana -title: Kibana +title: Deploy a custom Kibana instance description: Deploy custom Kibana for specific use cases with detailed step-by-step tutorials and configuration examples keywords: - kibana diff --git a/content/guides/lume-deno.md b/content/guides/lume-deno.md index ed555068c..78bd57ad9 100644 --- a/content/guides/lume-deno.md +++ b/content/guides/lume-deno.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Lume (Deno) -title: Lume (Deno) +title: Deploy a Lume website with Deno description: Build and deploy your website with Lume Static Site Generator (SSG) using Deno runtime on Clever Cloud platform with complete setup guide keywords: - lume diff --git a/content/guides/mdbook.md b/content/guides/mdbook.md index d3da8aa9e..16d4cd569 100644 --- a/content/guides/mdbook.md +++ b/content/guides/mdbook.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: mdBook -title: mdBook +title: Deploy an mdBook website description: Deploy mdBook Static Site Generator (SSG) websites on Clever Cloud with step-by-step tutorial and configuration guide keywords: - mdbook diff --git a/content/guides/mkdocs.md b/content/guides/mkdocs.md index bfe5ef025..f1a82d3fb 100644 --- a/content/guides/mkdocs.md +++ b/content/guides/mkdocs.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: MkDocs -title: MkDocs +title: Deploy a MkDocs website description: Deploy MkDocs Static Site Generator (SSG) websites on Clever Cloud with step-by-step tutorial and configuration guide keywords: - mkdocs diff --git a/content/guides/moodle.md b/content/guides/moodle.md index dc3893fc0..9313c46d2 100644 --- a/content/guides/moodle.md +++ b/content/guides/moodle.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Moodle -title: Moodle +title: Deploy a Moodle learning platform description: Deploy Moodle open source learning platform on Clever Cloud with detailed tutorials and best practices keywords: - moodle diff --git a/content/guides/node-js-mongo-db.md b/content/guides/node-js-mongo-db.md index 3de723436..a4d39910b 100644 --- a/content/guides/node-js-mongo-db.md +++ b/content/guides/node-js-mongo-db.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Node.js & MongoDB -title: Node.js & MongoDB +title: Deploy a Node.js application with MongoDB description: Deploy Node.js applications with MongoDB database on Clever Cloud with step-by-step tutorial and configuration guide keywords: - Node.js diff --git a/content/guides/node-statsd-nodejs-metrics.md b/content/guides/node-statsd-nodejs-metrics.md index 1e41f35a8..e7ecc4be3 100644 --- a/content/guides/node-statsd-nodejs-metrics.md +++ b/content/guides/node-statsd-nodejs-metrics.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Node-statsd -title: Node-statsd +title: Export Node.js metrics with node-statsd description: Configure node-statsd package on your Node.js application to push custom metrics with detailed step-by-step tutorials keywords: - Node.js diff --git a/content/guides/nuxt.md b/content/guides/nuxt.md index e4af0ec30..21d796ccc 100644 --- a/content/guides/nuxt.md +++ b/content/guides/nuxt.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Nuxt -title: Nuxt +title: Deploy a Nuxt application description: Deploy Nuxt as Static Site Generator (SSG) or with Node.js on Clever Cloud with step-by-step tutorial and configuration guide keywords: - nuxt diff --git a/content/guides/otree.md b/content/guides/otree.md index de1d3074b..61e34db97 100644 --- a/content/guides/otree.md +++ b/content/guides/otree.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: oTree -title: oTree +title: Deploy an oTree application description: Deploy oTree framework for multiplayer strategy games and complex surveys with detailed step-by-step tutorials keywords: - otree @@ -94,4 +94,3 @@ You can display your website’s URL or add a custom domain to it (you’ll need clever domain clever domain add your.website.tld ``` - diff --git a/content/guides/pgpool.md b/content/guides/pgpool.md index b78a0e209..cde7f3642 100644 --- a/content/guides/pgpool.md +++ b/content/guides/pgpool.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Pgpool-II -title: Pgpool-II +title: Configure Pgpool-II for PostgreSQL description: Configure and use Pgpool-II for PostgreSQL add-ons with detailed tutorials and best practices for deployment keywords: - pgpool diff --git a/content/guides/proxysql.md b/content/guides/proxysql.md index 326c4931e..9fc8b8790 100644 --- a/content/guides/proxysql.md +++ b/content/guides/proxysql.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: ProxySQL -title: ProxySQL +title: Configure ProxySQL for MySQL description: Configure and use ProxySQL for MySQL add-ons with detailed step-by-step tutorials and configuration examples keywords: - proxysql diff --git a/content/guides/python-django-sample.md b/content/guides/python-django-sample.md index 3306871a6..4797897c7 100644 --- a/content/guides/python-django-sample.md +++ b/content/guides/python-django-sample.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Django -title: Django +title: Deploy a Django application description: Deploy Django applications on Clever Cloud with detailed step-by-step tutorials and configuration examples keywords: - django diff --git a/content/guides/ruby-on-rails.md b/content/guides/ruby-on-rails.md index 681b3f4d8..4ac88e809 100644 --- a/content/guides/ruby-on-rails.md +++ b/content/guides/ruby-on-rails.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Ruby on Rails -title: Ruby on Rails +title: Deploy a Ruby on Rails application description: Deploy Ruby on Rails applications with comprehensive tutorial for web development framework deployment on Clever Cloud platform keywords: - ruby on rails diff --git a/content/guides/ruby-rack-app-tutorial.md b/content/guides/ruby-rack-app-tutorial.md index d3e494ba3..83a769a37 100644 --- a/content/guides/ruby-rack-app-tutorial.md +++ b/content/guides/ruby-rack-app-tutorial.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Ruby Rack (full tutorial) -title: Ruby Rack (full tutorial) +title: Build and deploy a Ruby Rack application description: Complete tutorial for deploying Ruby Rack applications on Clever Cloud with configuration examples and deployment best practices keywords: - ruby diff --git a/content/guides/ruby-rack.md b/content/guides/ruby-rack.md index 61b97020d..4323d5493 100644 --- a/content/guides/ruby-rack.md +++ b/content/guides/ruby-rack.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Ruby Rack -title: Ruby Rack +title: Deploy a Ruby Rack application description: Deploy Ruby Rack web applications on Clever Cloud with detailed step-by-step tutorials and configuration examples keywords: - ruby diff --git a/content/guides/sveltekit.md b/content/guides/sveltekit.md index 1f129b105..54e28dd2b 100644 --- a/content/guides/sveltekit.md +++ b/content/guides/sveltekit.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: SvelteKit -title: SvelteKit +title: Deploy a SvelteKit application description: Deploy a SvelteKit application as a static site, a single-page application, or a Node.js server on Clever Cloud keywords: - sveltekit diff --git a/content/guides/tutorial-drupal.md b/content/guides/tutorial-drupal.md index 474c3c06f..520eb884b 100644 --- a/content/guides/tutorial-drupal.md +++ b/content/guides/tutorial-drupal.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Drupal -title: Drupal +title: Deploy a Drupal website description: Deploy Drupal CMS on Clever Cloud with complete setup guide including database configuration, file management, and security setup keywords: - drupal diff --git a/content/guides/tutorial-laravel.md b/content/guides/tutorial-laravel.md index 8f45c542b..f2b3de230 100644 --- a/content/guides/tutorial-laravel.md +++ b/content/guides/tutorial-laravel.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Laravel -title: Laravel +title: Deploy a Laravel application description: Deploy Laravel PHP framework on Clever Cloud with step-by-step guide including database integration and configuration management keywords: - laravel diff --git a/content/guides/tutorial-symfony.md b/content/guides/tutorial-symfony.md index 905841cd7..b60d1404d 100644 --- a/content/guides/tutorial-symfony.md +++ b/content/guides/tutorial-symfony.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Symfony -title: Symfony +title: Deploy a Symfony application description: Deploy Symfony PHP framework on Clever Cloud with comprehensive setup guide including database configuration and asset management keywords: - symfony diff --git a/content/guides/tutorial-wordpress.md b/content/guides/tutorial-wordpress.md index f0f7b6568..44b5a328e 100644 --- a/content/guides/tutorial-wordpress.md +++ b/content/guides/tutorial-wordpress.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: WordPress -title: WordPress +title: Deploy a WordPress website description: Deploy WordPress website on Clever Cloud with complete database setup, file management, plugins configuration and optimization guide keywords: - wordpress From 524fc10eeda7a4638782aa8c46dbf36c01b5e072 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 01:25:58 +0200 Subject: [PATCH 123/214] docs(indexes): complete and harmonize navigation cards --- content/api/_index.md | 6 +-- content/doc/_index.md | 31 ++++++------ content/doc/addons/_index.md | 33 +++++++------ content/doc/addons/couchbase.md | 18 ------- content/doc/administrate/_index.md | 6 +-- content/doc/applications/_index.md | 18 +++---- content/doc/applications/java/_index.md | 11 ++--- content/doc/applications/php/_index.md | 19 ++++++-- content/doc/best-practices/_index.md | 5 +- content/doc/billing/_index.md | 2 +- content/doc/ci-cd/_index.md | 6 +-- content/doc/cli/_index.md | 12 ++--- content/doc/find-help/_index.md | 2 +- content/doc/reference/_index.md | 6 ++- content/guides/_index.md | 63 +++++++++++++------------ 15 files changed, 119 insertions(+), 119 deletions(-) delete mode 100644 content/doc/addons/couchbase.md diff --git a/content/api/_index.md b/content/api/_index.md index 536c1fb56..cedd7de49 100644 --- a/content/api/_index.md +++ b/content/api/_index.md @@ -26,8 +26,8 @@ aliases: {{< /hextra/hero-subtitle >}} {{< cards >}} - {{< card link="/developers/api/howto" title="How to use our API" subtitle="Learn more about Clever Cloud API, endpoints, auth and how to access it from multiple tools." icon="api" >}} - {{< card link="/developers/api/v2" title="Base v2 Endpoints" subtitle="Our base API endpoints with users, organisations, applications, add-ons, etc." icon="endpoints" >}} - {{< card link="/developers/api/v4" title="New v4 Endpoints" subtitle="More recent API endpoints with billing, deployments, load balancers, logs, etc." icon="new" >}} + {{< card link="/developers/api/howto" title="API overview" subtitle="Understand Clever Cloud APIs, authentication and available tools" icon="api" >}} + {{< card link="/developers/api/v2" title="APIv2 reference" subtitle="API endpoints for users, organisations, applications and add-ons" icon="endpoints" >}} + {{< card link="/developers/api/v4" title="APIv4 reference" subtitle="API endpoints for billing, deployments, load balancers and logs" icon="endpoints" >}} {{< /cards >}} diff --git a/content/doc/_index.md b/content/doc/_index.md index 50305fa7f..2bfb82022 100644 --- a/content/doc/_index.md +++ b/content/doc/_index.md @@ -29,20 +29,23 @@ aliases: {{< /hextra/hero-subtitle >}} {{< cards >}} - {{< card link="/developers/doc/quickstart" title="Getting Started" subtitle="Get ready to deploy and app in 5 minutes" icon="arrow-circle-right" >}} - {{< card link="/developers/doc/develop" title="Develop" subtitle="Best practices on Cloud deployments, sheduling jobs, etc." icon="code" >}} - {{< card link="/developers/doc/administrate" title="Administrate" subtitle="Access metrics, logs, manage domains, set up scalability…" icon="library" >}} - {{< card link="/developers/doc/applications" title="Deploy" subtitle="Deploy any app on Clever Cloud." icon="rocket-launch" >}} - {{< card link="/developers/doc/addons" title="Connect an add-on" subtitle="Connect a managed database and set up cloud storage." icon="plug" >}} - {{< card link="/developers/doc/find-help" title="Find help" subtitle="Lost? See how to get help." icon="support" >}} - {{< card link="/developers/doc/marketplace" title="Marketplace" subtitle="Integrate your own service on Clever Cloud" icon="puzzle" >}} - {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Deploy and manage your applications and dependencies from your terminal." icon="command-line" >}} - {{< card link="/developers/doc/ci-cd" title="CI/CD" subtitle="Deploy to Clever Cloud from GitHub or GitLab." icon="gitlab" >}} - - {{< card link="/developers/doc/reference" title="References" subtitle="List of environment variables your can use on Clever Clever Cloud, common configuration an all instances… " icon="collection" >}} - {{< card link="/developers/doc/account" title="User Account" subtitle="Set up your account, collaborate and manage permissions." icon="user-circle" >}} - {{< card link="/developers/doc/billing" title="Billing" subtitle="Find invoices, analyse consumption and understand pricing." icon="credit-card" >}} - {{< card link="/developers/doc/contribute" title="Contribute" subtitle="See how to contribute to this documentation." icon="pencil-alt" >}} + {{< card link="/developers/doc/quickstart" title="Getting started" subtitle="Deploy an application in five minutes" icon="arrow-circle-right" >}} + {{< card link="/developers/doc/applications" title="Applications" subtitle="Deploy applications with supported runtimes" icon="rocket-launch" >}} + {{< card link="/developers/doc/addons" title="Add-ons" subtitle="Connect managed databases, storage and services" icon="plug" >}} + {{< card link="/developers/doc/functions" title="Functions" subtitle="Deploy serverless functions compiled to WebAssembly" icon="code-bracket" >}} + {{< card link="/developers/doc/kubernetes" title="Kubernetes" subtitle="Create and manage Kubernetes clusters" icon="kubernetes" >}} + {{< card link="/developers/doc/develop" title="Develop" subtitle="Configure application behavior, networking and deployment hooks" icon="code" >}} + {{< card link="/developers/doc/administrate" title="Administrate" subtitle="Manage applications, domains, scaling and security" icon="library" >}} + {{< card link="/developers/doc/metrics" title="Metrics" subtitle="Monitor application performance and resource usage" icon="chart-bar" >}} + {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Manage applications and dependencies from your terminal" icon="command-line" >}} + {{< card link="/developers/doc/ci-cd" title="CI/CD" subtitle="Automate deployments from GitHub, GitLab or your own scripts" icon="git" >}} + {{< card link="/developers/doc/best-practices" title="Best practices" subtitle="Improve application security, performance and reliability" icon="checklist" >}} + {{< card link="/developers/doc/reference" title="Reference" subtitle="Find environment variables, common configuration and support matrices" icon="collection" >}} + {{< card link="/developers/doc/marketplace" title="Marketplace" subtitle="Integrate your own service with Clever Cloud" icon="puzzle" >}} + {{< card link="/developers/doc/account" title="User account" subtitle="Configure your account, organisations and permissions" icon="user-circle" >}} + {{< card link="/developers/doc/billing" title="Billing" subtitle="Find invoices, analyze consumption and understand pricing" icon="credit-card" >}} + {{< card link="/developers/doc/find-help" title="Find help" subtitle="Get support and troubleshoot platform issues" icon="support" >}} + {{< card link="/developers/doc/contribute" title="Contribute" subtitle="Contribute to the Clever Cloud documentation" icon="pencil-alt" >}} {{< /cards >}} {{< hextra/hero-subtitle style="margin:20px 0">}} diff --git a/content/doc/addons/_index.md b/content/doc/addons/_index.md index 29b058087..901834b8e 100644 --- a/content/doc/addons/_index.md +++ b/content/doc/addons/_index.md @@ -15,8 +15,10 @@ aliases: - /addons - /clever-cloud-overview/intro-to-addons - /doc/addons/clever-cloud-addons +- /doc/addons/couchbase/ - /doc/administrate/addons - /doc/clever-cloud-overview/intro-to-addons +- /doc/databases-and-services/couchbase - /doc/deploy/addon - /doc/extend - /doc/getting-started/dbaas @@ -27,32 +29,33 @@ Connect your application to an add-on: ## Databases {{< cards >}} - {{< card link="/developers/doc/addons/materia-kv" title="Materia KV" subtitle="Serverless & distributed key-value database" icon="materia" tag="Beta" >}} - {{< card link="/developers/doc/addons/mongodb" title="MongoDB" subtitle="The NoSQL document-oriented database" icon= "mongo">}} - {{< card link="/developers/doc/addons/mysql" title="MySQL" icon="mysql" subtitle="Your managed relational database" >}} - {{< card link="/developers/doc/addons/postgresql" title="PostgreSQL" icon="pg" subtitle="The not-only-SQL managed database" >}} - {{< card link="/developers/doc/addons/elastic" title="Elastic Stack" subtitle="Deploy your Elastic Stack in one click" icon="elastic" >}} - + {{< card link="/developers/doc/addons/elastic" title="Elastic Stack" subtitle="Managed search and analytics engine" icon="elastic" >}} + {{< card link="/developers/doc/addons/materia-kv" title="Materia KV" subtitle="Serverless distributed key-value database" icon="materia" tag="Beta" >}} + {{< card link="/developers/doc/addons/materia-ts" title="Materia TS" subtitle="Serverless distributed time-series database" icon="materia" tag="Private access" >}} + {{< card link="/developers/doc/addons/mongodb" title="MongoDB" subtitle="Managed NoSQL document database" icon="mongo" >}} + {{< card link="/developers/doc/addons/mysql" title="MySQL" subtitle="Managed relational database" icon="mysql" >}} + {{< card link="/developers/doc/addons/postgresql" title="PostgreSQL" subtitle="Managed object-relational database" icon="pg" >}} {{< card link="/developers/doc/addons/redis" title="Redis" subtitle="Managed key-value database" icon="redis" >}} {{< /cards >}} ## Storage & Messaging {{< cards >}} - {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="Object storage, compatible with S3 API" icon="cellar" >}} - {{< card link="/developers/doc/addons/fs-bucket" title="FS Bucket" subtitle="Persistent external file system for your apps" icon="fsbucket" >}} + {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="S3-compatible object storage" icon="cellar" >}} + {{< card link="/developers/doc/addons/fs-bucket" title="FS Bucket" subtitle="Persistent file system for applications" icon="fsbucket" >}} {{< card link="/developers/doc/addons/pulsar" title="Pulsar" subtitle="Open source, distributed messaging and streaming platform built for the cloud" icon="pulsar" >}} {{< /cards >}} ## Services & Tools {{< cards >}} - {{< card link="/developers/doc/addons/config-provider" title="Config Provider" subtitle="More freedom to manage, import and inject your configurations and credentials" icon="creds" >}} + {{< card link="/developers/doc/addons/config-provider" title="Config Provider" subtitle="Manage, import and inject configuration and credentials" icon="creds" >}} {{< card link="/developers/doc/addons/heptapod" title="Heptapod" subtitle="The friendly fork of GitLab Community Edition that adds support for Mercurial" icon="git" >}} - {{< card link="/developers/doc/addons/jenkins" title="Jenkins" subtitle="The leading open source automation server" icon="jenkins" >}} - {{< card link="/developers/doc/addons/keycloak" title="Keycloak" subtitle="Single sign-on with Identity and Access Management solution" icon="keycloak" >}} - {{< card link="/developers/doc/addons/mailpace" title="Mailpace" subtitle="Fast and reliable transactional email" icon="mail" >}} - {{< card link="/developers/doc/addons/matomo" title="Matomo" subtitle="A web analytics application as a service, the best Google Analytics alternative" icon="matomo" >}} - {{< card link="/developers/doc/addons/metabase" title="Metabase" subtitle="An easy business intelligence tool to query and visualize data" icon="metabase" >}} - {{< card link="/developers/doc/addons/otoroshi" title="Otoroshi with LLM" subtitle="Simple API management based on a modern reverse proxy with preconigured plugins" icon="endpoints" >}} + {{< card link="/developers/doc/addons/jenkins" title="Jenkins" subtitle="Open source automation server" icon="jenkins" >}} + {{< card link="/developers/doc/addons/keycloak" title="Keycloak" subtitle="Identity and access management with single sign-on" icon="keycloak" >}} + {{< card link="/developers/doc/addons/kms" title="KMS" subtitle="Distributed secrets management service" icon="key" tag="Private access" >}} + {{< card link="/developers/doc/addons/mailpace" title="MailPace" subtitle="Fast and reliable transactional email" icon="mail" >}} + {{< card link="/developers/doc/addons/matomo" title="Matomo" subtitle="Privacy-focused web analytics platform" icon="matomo" >}} + {{< card link="/developers/doc/addons/metabase" title="Metabase" subtitle="Business intelligence and data visualization platform" icon="metabase" >}} + {{< card link="/developers/doc/addons/otoroshi" title="Otoroshi with LLM" subtitle="API management based on a modern reverse proxy with preconfigured plugins" icon="endpoints" >}} {{< /cards >}} diff --git a/content/doc/addons/couchbase.md b/content/doc/addons/couchbase.md deleted file mode 100644 index 05a0d9ac2..000000000 --- a/content/doc/addons/couchbase.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -type: docs -linkTitle: CouchBase -title: CouchBase -description: Deploy Couchbase NoSQL database add-on service on Clever Cloud with automatic scaling, backup capabilities, and enterprise support -keywords: -- couchbase -- nosql -- database -- addon -- beta -- document-store -aliases: -- /doc/databases-and-services/couchbase ---- - -CouchBase is a database wich automation is still in beta on Clever Cloud. -Please reach to our support if you would like to use this database, they will kindly assist you. diff --git a/content/doc/administrate/_index.md b/content/doc/administrate/_index.md index 239e4c090..be3531369 100644 --- a/content/doc/administrate/_index.md +++ b/content/doc/administrate/_index.md @@ -16,17 +16,17 @@ aliases: --- {{< cards >}} - {{< card link="/developers/doc/administrate/apps-management" title="Application management" icon="cursor-arrow-ripple" >}} {{< card link="/developers/doc/administrate/activity" title="Activity" icon="clock" >}} + {{< card link="/developers/doc/administrate/apps-management" title="Application management" icon="cursor-arrow-ripple" >}} {{< card link="/developers/doc/administrate/scalability" title="Application scaling" icon="arrow-trending-up" >}} - {{< card link="/developers/doc/administrate/clamav" title="CLAMAV" icon="clamav" >}} + {{< card link="/developers/doc/administrate/clamav" title="ClamAV" icon="clamav" >}} {{< card link="/developers/doc/administrate/cron" title="CRON" icon="code-bracket" >}} {{< card link="/developers/doc/administrate/database-migration" title="Database migration and security update" icon="circle-stack" >}} {{< card link="/developers/doc/administrate/domain-names" title="Domain names" icon="at-symbol" >}} {{< card link="/developers/doc/administrate/encryption-at-rest" title="Encryption at rest" icon="lock-closed" >}} {{< card link="/developers/doc/administrate/ssl" title="Installing TLS certificate" icon="document-check" >}} {{< card link="/developers/doc/administrate/log-management" title="Log management" icon="document-text" >}} - {{< card link="/developers/doc/administrate/network" title="Network and IP adresses ranges" icon="globe-alt" >}} + {{< card link="/developers/doc/administrate/network" title="Networking and IP addresses" icon="globe-alt" >}} {{< card link="/developers/doc/administrate/service-dependencies" title="Service dependencies" icon="plug" >}} {{< card link="/developers/doc/administrate/ssh-clever-tools" title="SSH access to running instances" icon="command-line" >}} {{< card link="/developers/doc/administrate/tcp-redirections" title="TCP redirections with Clever Tools" icon="tcp-ip-service" >}} diff --git a/content/doc/applications/_index.md b/content/doc/applications/_index.md index 3a50c87c5..26f5c42b9 100644 --- a/content/doc/applications/_index.md +++ b/content/doc/applications/_index.md @@ -32,10 +32,10 @@ Find here specific instructions related to your application's language. {{< card link="/developers/doc/applications/dotnet" title=".NET" icon="dotnet" >}} {{< card link="/developers/doc/applications/docker" title="Docker" icon="docker" >}} {{< card link="/developers/doc/applications/elixir" title="Elixir" icon="elixir" >}} - {{< card link="/developers/doc/applications/frankenphp" title="Franken PHP" icon="frankenphp" >}} + {{< card link="/developers/doc/applications/frankenphp" title="FrankenPHP" icon="frankenphp" >}} {{< card link="/developers/doc/applications/golang" title="Go" icon="go" >}} - {{< card link="/developers/doc/applications/haskell" title="Haskell" icon="haskell">}} - {{< card link="/developers/doc/applications/java" title="Java (Gradle, Jar, Maven, War/Ear)" icon="java" >}} + {{< card link="/developers/doc/applications/haskell" title="Haskell" icon="haskell" >}} + {{< card link="/developers/doc/applications/java" title="Java (Gradle, JAR, Maven, WAR/EAR)" icon="java" >}} {{< card link="/developers/doc/applications/linux" title="Linux" icon="linux" >}} {{< card link="/developers/doc/applications/meteor" title="Meteor.js" icon="meteor" >}} {{< card link="/developers/doc/applications/nodejs" title="Node.js & Bun" icon="node" >}} @@ -63,12 +63,12 @@ Some runtimes can be extended with additional modules (such as extra PHP extensi You can control deployments and set your application configuration with environment variables: {{< cards >}} - {{< card link="/developers/doc/reference/reference-environment-variables/#set-by-the-deployment-process" title="Common to all applications" subtitle="Set by the deployment process." icon="server-stack" >}} - {{< card link="/developers/doc/reference/reference-environment-variables/#variables-you-can-define" title="Define" subtitle="Variables you can define." icon="pencil-square" >}} - {{< card link="/developers/doc/reference/reference-environment-variables/#control-build-and-dependencies-cache" title="Cache" subtitle="Control the build and deployment cache." icon="arrow-up-on-square-stack" >}} - {{< card link="/developers/doc/reference/reference-environment-variables/#control-the-deployments-behavior" title="Run" subtitle="Control the deployment behavior." icon="play-circle" >}} - {{< card link="/developers/doc/reference/reference-environment-variables/#deployment-hooks" title="Hooks" subtitle="Define commands to run between various steps of the deployment." icon="eye-dropper" >}} - {{< card link="/developers/doc/reference/reference-environment-variables" title="How to" subtitle="How to to configure your application with environment variables." icon="book-open" >}} + {{< card link="/developers/doc/develop/env-variables" title="Environment variables" subtitle="Understand how variables are defined and used" icon="book-open" >}} + {{< card link="/developers/doc/reference/reference-environment-variables/#set-by-the-deployment-process" title="Platform variables" subtitle="Review variables set by the deployment process" icon="server-stack" >}} + {{< card link="/developers/doc/reference/reference-environment-variables/#variables-you-can-define" title="Configuration variables" subtitle="Review variables you can define" icon="pencil-square" >}} + {{< card link="/developers/doc/develop/env-variables/#settings-you-can-define-using-environment-variables" title="Build cache" subtitle="Control which files are included in the build cache" icon="arrow-up-on-square-stack" >}} + {{< card link="/developers/doc/reference/reference-environment-variables/#variables-you-can-define" title="Run command" subtitle="Override the command used to run your application" icon="play-circle" >}} + {{< card link="/developers/doc/develop/build-hooks" title="Deployment hooks" subtitle="Run commands during deployment phases" icon="rocket-launch" >}} {{< /cards >}} ### Common to All Applications diff --git a/content/doc/applications/java/_index.md b/content/doc/applications/java/_index.md index 5227eb99d..10e345e03 100644 --- a/content/doc/applications/java/_index.md +++ b/content/doc/applications/java/_index.md @@ -28,11 +28,11 @@ Find detailed instructions according to your framework {{< cards >}} {{< card link="/developers/doc/applications/java/java-gradle" title="Gradle" icon="gradle" >}} - {{< card link="/developers/doc/applications/java/java-jar" title="Jar" icon="java" >}} - {{< card link="/developers/doc/applications/java/java-maven" title="Maven" icon= "maven">}} - {{< card link="/developers/doc/applications/java/java-war" title="War/Ear" icon="java" >}} - {{< card link="/developers/doc/applications/scala/play-framework-1" title="Play 1" icon="play" >}} - {{< card link="/developers/doc/applications/scala/play-framework-2" title="Play 2" icon="play" >}} + {{< card link="/developers/doc/applications/java/java-jar" title="JAR" icon="java" >}} + {{< card link="/developers/doc/applications/java/java-maven" title="Maven" icon="maven" >}} + {{< card link="/developers/doc/applications/java/java-war" title="WAR/EAR" icon="java" >}} + {{< card link="/developers/doc/applications/scala/play-framework-1" title="Play Framework 1.x" icon="play" >}} + {{< card link="/developers/doc/applications/scala/play-framework-2" title="Play Framework 2.x" icon="play" >}} {{< /cards >}} @@ -47,4 +47,3 @@ Looking for instructions on a specific framework deployment? icon="science" >}} {{< /cards >}} - diff --git a/content/doc/applications/php/_index.md b/content/doc/applications/php/_index.md index 6bc1eb3bc..7cf2bd727 100644 --- a/content/doc/applications/php/_index.md +++ b/content/doc/applications/php/_index.md @@ -152,6 +152,15 @@ If `MEMORY_LIMIT` exceeds the maximum value for the selected flavor, Clever Clou On XS and larger flavors, setting [`CC_CONFIGURATION_PM_MAX_CHILDREN`](/doc/reference/reference-environment-variables#php) can lower the calculated memory limit. On Nano, the platform always uses six workers. If you also set a valid `MEMORY_LIMIT`, it takes precedence over the adjustment based on the number of workers. +### Configuration reference + +{{< cards >}} + {{< card link="/developers/doc/applications/php/apache" title="Apache" subtitle="Configure the web server, headers and authentication" icon="feather" >}} + {{< card link="/developers/doc/applications/php/composer" title="Composer" subtitle="Manage PHP dependencies and private repositories" icon="php" >}} + {{< card link="/developers/doc/applications/php/extensions" title="Extensions" subtitle="Review and configure available PHP extensions" icon="puzzle" >}} + {{< card link="/developers/doc/applications/php/sessions-emails" title="Sessions and emails" subtitle="Configure session storage and email delivery" icon="mail" >}} +{{< /cards >}} + ## Frameworks and CMS The following is the list of tested CMS by our team. @@ -159,11 +168,11 @@ The following is the list of tested CMS by our team. It's quite not exhaustive, so it does not mean that other CMS can't work on the Clever Cloud platform. {{< cards >}} - {{< card link="/developers/guides/tutorial-drupal" title="Drupal" subtitle= "Deploy a Drupal-based website on Clever Cloud" icon="drupal" >}} - {{< card link="/developers/guides/tutorial-laravel" title="Laravel" subtitle= "Deploy a Laravel app on Clever Cloud" icon="laravel" >}} - {{< card link="/developers/guides/tutorial-symfony" title="Symfony" subtitle= "Deploy a Symfony application on Clever Cloud" icon="symfony" >}} - {{< card link="/developers/guides/tutorial-wordpress" title="WordPress" subtitle= "Deploy WordPress on Clever Cloud" icon="wordpress" >}} - {{< card link="/developers/guides/moodle" title="Moodle" subtitle="Full Moodle installation and configuration guide" icon="moodle" >}} + {{< card link="/developers/guides/tutorial-drupal" title="Drupal" subtitle="Deploy a Drupal website on Clever Cloud" icon="drupal" >}} + {{< card link="/developers/guides/tutorial-laravel" title="Laravel" subtitle="Deploy a Laravel application on Clever Cloud" icon="laravel" >}} + {{< card link="/developers/guides/moodle" title="Moodle" subtitle="Deploy and configure a Moodle learning platform on Clever Cloud" icon="moodle" >}} + {{< card link="/developers/guides/tutorial-symfony" title="Symfony" subtitle="Deploy a Symfony application on Clever Cloud" icon="symfony" >}} + {{< card link="/developers/guides/tutorial-wordpress" title="WordPress" subtitle="Deploy a WordPress website on Clever Cloud" icon="wordpress" >}} {{< /cards >}} diff --git a/content/doc/best-practices/_index.md b/content/doc/best-practices/_index.md index b2e625b4b..c4955a8a9 100644 --- a/content/doc/best-practices/_index.md +++ b/content/doc/best-practices/_index.md @@ -19,6 +19,7 @@ aliases: {{< cards >}} {{< card link="/developers/doc/best-practices/blue-green" title="Blue/green deployment" icon="traffic-light" >}} {{< card link="/developers/doc/best-practices/cloud-storage" title="Cloud storage" icon="cloud-arrow-up" >}} - {{< card link="/developers/doc/best-practices/load-testing" title="Load testing" icon= "chart-bar-square">}} - {{< card link="/developers/doc/best-practices/12-factors" title="The Twelve-Factor app " icon="checklist" >}} + {{< card link="/developers/doc/best-practices/load-testing" title="Load testing" icon="chart-bar-square" >}} + {{< card link="/developers/doc/best-practices/12-factors" title="The Twelve-Factor App" icon="checklist" >}} + {{< card link="/developers/doc/best-practices/tips_and_tricks" title="Tips & Tricks" icon="adjustments-horizontal" >}} {{< /cards >}} diff --git a/content/doc/billing/_index.md b/content/doc/billing/_index.md index eb5299848..060038253 100644 --- a/content/doc/billing/_index.md +++ b/content/doc/billing/_index.md @@ -21,7 +21,7 @@ aliases: --- {{< cards >}} - {{< card link="/developers/doc/billing/analytics-consumption" title="Analytics consumption" icon="chart-bar" >}} + {{< card link="/developers/doc/billing/analytics-consumption" title="Analytics and consumption" icon="chart-bar" >}} {{< card link="/developers/doc/billing/payments-invoicing" title="Payments & invoicing" icon="banknotes" >}} {{< card link="https://www.clever.cloud/pricing/" title="Pricing" icon="currency-euro" >}} {{< card link="/developers/doc/billing/unified-invoices" title="Unified invoicing" icon="user-group" >}} diff --git a/content/doc/ci-cd/_index.md b/content/doc/ci-cd/_index.md index de2c7b1ff..5883fe8b8 100644 --- a/content/doc/ci-cd/_index.md +++ b/content/doc/ci-cd/_index.md @@ -21,7 +21,7 @@ aliases: {{< /hextra/hero-subtitle >}} {{< cards >}} - {{< card link="/developers/doc/ci-cd/github" title="Deploy from GitHub" subtitle="Deploy directly from GitHub and automate review apps." icon="github" >}} - {{< card link="/developers/doc/ci-cd/gitlab" title="Deploy from GitLab" subtitle="Deploy directly from GitLab and automate review apps." icon="gitlab" >}} - {{< card link="/developers/doc/ci-cd/custom-scripts" title="Write your own CI/CD" subtitle="Write your own scripts to deploy to Clever Cloud and automate tasks." icon="command-line" >}} + {{< card link="/developers/doc/ci-cd/github" title="Deploy from GitHub" subtitle="Deploy directly from GitHub and automate review apps" icon="github" >}} + {{< card link="/developers/doc/ci-cd/gitlab" title="Deploy from GitLab" subtitle="Deploy directly from GitLab and automate review apps" icon="gitlab" >}} + {{< card link="/developers/doc/ci-cd/custom-scripts" title="Write your own CI/CD" subtitle="Write scripts to deploy to Clever Cloud and automate tasks" icon="command-line" >}} {{< /cards >}} diff --git a/content/doc/cli/_index.md b/content/doc/cli/_index.md index 42c8deb91..eb29d6731 100644 --- a/content/doc/cli/_index.md +++ b/content/doc/cli/_index.md @@ -49,16 +49,16 @@ You'll find below the first commands to know to connect Clever Tools to your acc {{< cards >}} {{< card link="/developers/doc/cli/install" title="Install" icon="arrow-down-tray" >}} - {{< card link="/developers/doc/cli/addons" title="Create and manage add-ons" icon= "wrench-screwdriver" >}} + {{< card link="/developers/doc/cli/addons" title="Create and manage add-ons" icon="wrench-screwdriver" >}} {{< card link="/developers/doc/cli/applications" title="Create and manage applications" icon="code-bracket" >}} + {{< card link="/developers/doc/cli/kubernetes" title="Kubernetes" icon="kubernetes" >}} {{< card link="/developers/doc/cli/kv-stores" title="Manage KV stores" icon="server-stack" >}} - {{< card link="/developers/doc/cli/logs-drains" title="Manage logs, drains" icon="command-line" >}} + {{< card link="/developers/doc/cli/logs-drains" title="Manage logs and drains" icon="command-line" >}} {{< card link="/developers/doc/cli/network-groups" title="Network Groups" icon="tcp-ip-service" >}} - {{< card link="/developers/doc/cli/notifications-webhooks" title="Notifications, Web hooks" icon="bell" >}} - {{< card link="/developers/doc/cli/operators" title="Operators (Managed services)" icon="document-check" >}} + {{< card link="/developers/doc/cli/notifications-webhooks" title="Notifications and webhooks" icon="bell" >}} + {{< card link="/developers/doc/cli/operators" title="Operators (managed services)" icon="document-check" >}} {{< card link="/developers/doc/cli/profiles" title="Profiles and overrides" icon="user" >}} - {{< card link="/developers/doc/cli/services-depedencies" title="Services dependencies" icon="endpoints" >}} - {{< card link="/developers/doc/cli/kubernetes" title="Kubernetes" icon="server-stack" >}} + {{< card link="/developers/doc/cli/services-depedencies" title="Service dependencies" icon="endpoints" >}} {{< /cards >}} ## basic commands diff --git a/content/doc/find-help/_index.md b/content/doc/find-help/_index.md index 39fce70de..d122b8fd6 100644 --- a/content/doc/find-help/_index.md +++ b/content/doc/find-help/_index.md @@ -16,7 +16,7 @@ aliases: --- {{< cards >}} - {{< card link="/developers/doc/find-help/support" title="Support" icon="lifebuoy" >}} {{< card link="/developers/doc/find-help/faq" title="FAQ" icon="question-mark-circle" >}} + {{< card link="/developers/doc/find-help/support" title="Support" icon="lifebuoy" >}} {{< card link="/developers/doc/find-help/troubleshooting" title="Troubleshooting" icon="magnifying-glass" >}} {{< /cards >}} diff --git a/content/doc/reference/_index.md b/content/doc/reference/_index.md index 8eb3252bf..92ba39323 100644 --- a/content/doc/reference/_index.md +++ b/content/doc/reference/_index.md @@ -16,6 +16,8 @@ aliases: --- {{< cards >}} - {{< card link="/developers/doc/reference/common-configuration" title="Common configuration for all instances" icon="cog-6-tooth" >}} - {{< card link="/developers/doc/reference/reference-environment-variables" title="All environment variables" icon="creds" >}} + {{< card link="/developers/doc/reference/cli" title="CLI reference" icon="command-line" >}} + {{< card link="/developers/doc/reference/common-configuration" title="Common configuration" icon="cog-6-tooth" >}} + {{< card link="/developers/doc/reference/reference-environment-variables" title="Environment variable reference" icon="creds" >}} + {{< card link="/developers/doc/reference/products-support" title="Product support on tools" icon="document-check" >}} {{< /cards >}} diff --git a/content/guides/_index.md b/content/guides/_index.md index 02f7d1813..5a0a722f6 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -26,37 +26,38 @@ llmsTxtOptional: true Step by steps guides to deploy and use your favorite stack on Clever Cloud {{< /hextra/hero-subtitle >}} {{< cards >}} - {{< card link="/developers/guides/astro" title="Astro" subtitle= "Build and deploy a Astro application on Clever Cloud" icon="astro" >}} - {{< card link="/developers/guides/python-django-sample" title="Django" subtitle= "Configure your Django application to run on Clever Cloud" icon="django" >}} - {{< card link="/developers/guides/docs" title="Docs" subtitle= "Deploy the open source document and live collaboration editor on Clever Cloud" icon="docs-suite" >}} - {{< card link="/developers/guides/docusaurus" title="Docusaurus" subtitle= "Build and deploy a static Docusaurus based website on Clever Cloud" icon="docusaurus" >}} - {{< card link="/developers/guides/tutorial-drupal" title="Drupal" subtitle= "Deploy a Drupal-based website on Clever Cloud" icon="drupal" >}} - {{< card link="/developers/guides/ekg-statsd-haskell-metrics/" title="ekg-statsd" subtitle= "How to configure ekg-statsd package in your Haskell application" icon="haskell" >}} - {{< card link="/developers/guides/eleventy" title="Eleventy (11ty)" subtitle= "Build and deploy a static Eleventy (11ty) based website on Clever Cloud" icon="11ty" >}} - {{< card link="/developers/guides/fluentd/" title="Fluentd" subtitle= "How to deploy Fluentd using Docker on Clever Cloud" icon="fluentd" >}} - {{< card link="/developers/guides/go-echoip" title="Echo IP" subtitle= "How to deploy EchoIP based on a Go application on Clever Cloud" icon="go" >}} - {{< card link="/developers/guides/hexo" title="Hexo" subtitle= "Build and deploy a static Hexo based website on Clever Cloud" icon="hexo" >}} - {{< card link="/developers/guides/hono" title="Hono" subtitle= "Deploy a Hono application with Node.js on Clever Cloud" icon="hono" >}} - {{< card link="/developers/guides/hugo" title="Hugo" subtitle= "Build and deploy a static Hugo based website on Clever Cloud" icon="hugo" >}} - {{< card link="/developers/guides/hugo-static-s3" title="Hugo + Cellar" subtitle= "Build and deploy a static Hugo based website and host it using Cellar" icon="hugo" >}} - {{< card link="/developers/guides/kibana" title="Kibana" subtitle= "Deploy and customize a Kibana server on Clever Cloud" icon="kibana" >}} - {{< card link="/developers/guides/kubernetes-operator" title="Kubernetes Operator" subtitle= "Manage your Clever Cloud databases and addons seamlessly from Kubernetes" icon="kubernetes" >}} - {{< card link="/developers/guides/tutorial-laravel" title="Laravel" subtitle= "Deploy a Laravel app on Clever Cloud" icon="laravel" >}} - {{< card link="/developers/guides/lume-deno" title="Lume (Deno)" subtitle= "Build and deploy a static Lume (Deno) based website on Clever Cloud" icon="deno" >}} - {{< card link="/developers/guides/mdbook" title="mdBook" subtitle= "Build and deploy a static mbBook based website on Clever Cloud" icon="mdbook" >}} - {{< card link="/developers/guides/mkdocs" title="MkDocs" subtitle= "Build and deploy a static MkDocs based website on Clever Cloud" icon="docs" >}} - {{< card link="/developers/guides/moodle" title="Moodle" subtitle="Full Moodle installation and configuration guide" icon="moodle" >}} - {{< card link="/developers/guides/node-js-mongo-db" title="Node.js + MongoDB starter" subtitle= "Starter tutorial to deploy a Node.js + MongoDB application on Clever Cloud" icon="node" >}} - {{< card link="/developers/guides/node-statsd-nodejs-metrics" title="node-statsd" subtitle= "Configure node-statsd package on your Node.js application to push custom metrics" icon="node" >}} - {{< card link="/developers/guides/nuxt" title="Nuxt" subtitle= "Build and deploy a Nuxt application on Clever Cloud" icon="nuxt" >}} - {{< card link="/developers/guides/outline" title="Outline" subtitle= "Deploy the Outline knowledge base on Clever Cloud" icon="outline" >}} - {{< card link="/developers/guides/pgpool" title="Pgpool-II" subtitle= "How to configure and use Pgpool-II for PostgreSQL add-ons" icon="pg" >}} - {{< card link="/developers/guides/proxysql" title="ProxySQL" subtitle= "Configure and use ProxySQL for MySQL add-ons" icon="mysql" >}} - {{< card link="/developers/guides/ruby-on-rails" title="Ruby On Rails" subtitle= "How to deploy Ruby on Rails framework" icon="ruby" >}} - {{< card link="/developers/guides/ruby-rack" title="Ruby Rack" subtitle= "Set up a Ruby Rack application and deploy on Clever Cloud" icon="ruby" >}} - {{< card link="/developers/guides/ruby-rack-app-tutorial" title="Ruby Rack (full tutorial)" subtitle= "Write a hello world web application using Rack and deploy it on Clever Cloud" icon="ruby" >}} + {{< card link="/developers/guides/astro" title="Astro" subtitle="Deploy an Astro application as a static site or with Node.js" icon="astro" >}} + {{< card link="/developers/guides/python-django-sample" title="Django" subtitle="Deploy a Django application" icon="django" >}} + {{< card link="/developers/guides/docs" title="Docs" subtitle="Deploy the Docs collaborative editor" icon="docs-suite" >}} + {{< card link="/developers/guides/docusaurus" title="Docusaurus" subtitle="Deploy a static Docusaurus website" icon="docusaurus" >}} + {{< card link="/developers/guides/tutorial-drupal" title="Drupal" subtitle="Deploy a Drupal website" icon="drupal" >}} + {{< card link="/developers/guides/go-echoip" title="EchoIP" subtitle="Deploy EchoIP as a Go application" icon="go" >}} + {{< card link="/developers/guides/ekg-statsd-haskell-metrics" title="Ekg-statsd" subtitle="Export Haskell metrics with ekg-statsd" icon="haskell" >}} + {{< card link="/developers/guides/eleventy" title="Eleventy (11ty)" subtitle="Deploy a static Eleventy website" icon="11ty" >}} + {{< card link="/developers/guides/fluentd" title="Fluentd" subtitle="Deploy Fluentd with Docker" icon="fluentd" >}} + {{< card link="/developers/guides/hexo" title="Hexo" subtitle="Deploy a static Hexo website" icon="hexo" >}} + {{< card link="/developers/guides/hono" title="Hono" subtitle="Deploy a Hono application with Node.js" icon="hono" >}} + {{< card link="/developers/guides/hugo" title="Hugo" subtitle="Deploy a static Hugo website" icon="hugo" >}} + {{< card link="/developers/guides/hugo-static-s3" title="Hugo + Cellar" subtitle="Build a Hugo website and host it with Cellar" icon="hugo" >}} + {{< card link="/developers/guides/kibana" title="Kibana" subtitle="Deploy and customize a Kibana server" icon="kibana" >}} + {{< card link="/developers/doc/kubernetes/operator" title="Kubernetes Operator" subtitle="Manage Clever Cloud add-ons from Kubernetes" icon="kubernetes" >}} + {{< card link="/developers/guides/tutorial-laravel" title="Laravel" subtitle="Deploy a Laravel application" icon="laravel" >}} + {{< card link="/developers/guides/lume-deno" title="Lume (Deno)" subtitle="Deploy a static Lume website with Deno" icon="deno" >}} + {{< card link="/developers/guides/mdbook" title="mdBook" subtitle="Deploy a static mdBook website" icon="mdbook" >}} + {{< card link="/developers/guides/mkdocs" title="MkDocs" subtitle="Deploy a static MkDocs website" icon="docs" >}} + {{< card link="/developers/guides/moodle" title="Moodle" subtitle="Deploy and configure a Moodle learning platform" icon="moodle" >}} + {{< card link="/developers/guides/node-js-mongo-db" title="Node.js & MongoDB" subtitle="Deploy a Node.js application with MongoDB" icon="node" >}} + {{< card link="/developers/guides/node-statsd-nodejs-metrics" title="Node-statsd" subtitle="Export Node.js metrics with node-statsd" icon="node" >}} + {{< card link="/developers/guides/nuxt" title="Nuxt" subtitle="Deploy a Nuxt application as a static site or with Node.js" icon="nuxt" >}} + {{< card link="/developers/guides/otree" title="oTree" subtitle="Deploy an oTree application" icon="django" >}} + {{< card link="/developers/guides/outline" title="Outline" subtitle="Deploy the Outline knowledge base" icon="outline" >}} + {{< card link="/developers/guides/pgpool" title="Pgpool-II" subtitle="Configure Pgpool-II for PostgreSQL add-ons" icon="pg" >}} + {{< card link="/developers/guides/proxysql" title="ProxySQL" subtitle="Configure ProxySQL for MySQL add-ons" icon="mysql" >}} + {{< card link="/developers/guides/ruby-on-rails" title="Ruby on Rails" subtitle="Deploy a Ruby on Rails application" icon="ruby" >}} + {{< card link="/developers/guides/ruby-rack" title="Ruby Rack" subtitle="Deploy a Ruby Rack application" icon="ruby" >}} + {{< card link="/developers/guides/ruby-rack-app-tutorial" title="Ruby Rack (full tutorial)" subtitle="Build and deploy a Ruby Rack application" icon="ruby" >}} {{< card link="/developers/guides/sftpgo" title="SFTPGo" subtitle="Deploy an SFTP server with PostgreSQL and Cellar object storage" icon="server-stack" >}} {{< card link="/developers/guides/sveltekit" title="SvelteKit" subtitle="Deploy a SvelteKit application with static or server-side rendering" icon="svelte" >}} - {{< card link="/developers/guides/tutorial-symfony" title="Symfony" subtitle= "Deploy a Symfony application on Clever Cloud" icon="symfony" >}} - {{< card link="/developers/guides/tutorial-wordpress" title="WordPress" subtitle= "Deploy WordPress on Clever Cloud" icon="wordpress" >}} + {{< card link="/developers/guides/tutorial-symfony" title="Symfony" subtitle="Deploy a Symfony application" icon="symfony" >}} + {{< card link="/developers/guides/tutorial-wordpress" title="WordPress" subtitle="Deploy a WordPress website" icon="wordpress" >}} {{< /cards >}} From fafc1b8d062d8bbf2614f2ec89a47d2878670f25 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 01:30:38 +0200 Subject: [PATCH 124/214] addons(cellar): replace deprecated tabs syntax --- content/doc/addons/cellar.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/doc/addons/cellar.md b/content/doc/addons/cellar.md index 72630432c..cedb35a8a 100644 --- a/content/doc/addons/cellar.md +++ b/content/doc/addons/cellar.md @@ -842,9 +842,9 @@ To ensure the integrity of the uploaded file, you can add a checksum as part of Cellar supports pre-signed URLs and MD5 checksum validation. If you pre-sign your URL with an MD5 checksum as a parameter, Cellar validates the uploaded file against the expected checksum by verifying both the request headers and the file content. The upload fail if either value doesn't match the expected checksum. -{{< tabs items="Python, Node.js" >}} +{{< tabs >}} - {{< tab >}} + {{< tab name="Python" icon="python" >}} ```python import boto3 @@ -890,7 +890,7 @@ Cellar supports pre-signed URLs and MD5 checksum validation. If you pre-sign you {{< /tab >}} - {{< tab >}} + {{< tab name="Node.js" icon="node" >}} ```js import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; From ff1ffcf5474575f26b0fbbfdf5802a451c6aaf79 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 01:36:15 +0200 Subject: [PATCH 125/214] fix(cards): align content to the top --- assets/css/custom.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/css/custom.css b/assets/css/custom.css index f9a9d0ae0..fc936d9c8 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -52,6 +52,11 @@ html:not([class~="dark"]) .sidebar-active-item { Shortcodes ----------------------------- */ +/* Align card content to the top when cards have different heights */ +.hextra-card > .hx\:mt-auto { + margin-top: 0; +} + /* Hero buttons on home page */ .btn-primary { background-color: var(--purple-color); @@ -693,4 +698,3 @@ html[class~="dark"] .content table:not(.code-block table) tbody tr:nth-child(odd html[class~="dark"] .swagger-ui .responses-wrapper .response-header .response-toggle svg { fill: rgb(209 213 219); } - \ No newline at end of file From e8a32d55f944ebddd33677d8450620d78a65cc3f Mon Sep 17 00:00:00 2001 From: Julie POUNY Date: Thu, 10 Jul 2025 15:29:16 +0200 Subject: [PATCH 126/214] guides: add Ghost Co-Authored-By: David Legrand --- content/guides/_index.md | 1 + content/guides/ghost.md | 281 +++++++++++++++++++++++++++++++++++++++ data/icons.yaml | 1 + 3 files changed, 283 insertions(+) create mode 100644 content/guides/ghost.md diff --git a/content/guides/_index.md b/content/guides/_index.md index 5a0a722f6..3a9c7df5f 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -35,6 +35,7 @@ llmsTxtOptional: true {{< card link="/developers/guides/ekg-statsd-haskell-metrics" title="Ekg-statsd" subtitle="Export Haskell metrics with ekg-statsd" icon="haskell" >}} {{< card link="/developers/guides/eleventy" title="Eleventy (11ty)" subtitle="Deploy a static Eleventy website" icon="11ty" >}} {{< card link="/developers/guides/fluentd" title="Fluentd" subtitle="Deploy Fluentd with Docker" icon="fluentd" >}} + {{< card link="/developers/guides/ghost" title="Ghost" subtitle="Deploy a publishing platform with MySQL and object storage" icon="ghost" >}} {{< card link="/developers/guides/hexo" title="Hexo" subtitle="Deploy a static Hexo website" icon="hexo" >}} {{< card link="/developers/guides/hono" title="Hono" subtitle="Deploy a Hono application with Node.js" icon="hono" >}} {{< card link="/developers/guides/hugo" title="Hugo" subtitle="Deploy a static Hugo website" icon="hugo" >}} diff --git a/content/guides/ghost.md b/content/guides/ghost.md new file mode 100644 index 000000000..629f8ac8f --- /dev/null +++ b/content/guides/ghost.md @@ -0,0 +1,281 @@ +--- +type: docs +linkTitle: Ghost +title: Deploy a self-hosted Ghost 6 publication +description: Deploy Ghost on Clever Cloud with MySQL, Cellar object storage and an FS Bucket +keywords: +- blogging +- cellar +- ghost +- linux +- mise +- mysql +- publishing +- s3 storage +--- + +{{< hextra/hero-subtitle >}} + Deploy Ghost, an open source publishing platform, on Clever Cloud. +{{< /hextra/hero-subtitle >}} + +[Ghost](https://ghost.org/) is a publishing platform for websites, memberships and newsletters. This guide deploys Ghost on the [Linux runtime](/developers/doc/applications/linux/) with [Mise](https://mise.jdx.dev/), [MySQL](/developers/doc/addons/mysql/), [Cellar S3-compatible object storage](/developers/doc/addons/cellar/) and an [FS Bucket](/developers/doc/addons/fs-bucket/). + +MySQL stores publications, users and settings. Cellar stores uploaded images, media and files through Ghost's built-in S3 storage adapter. The FS Bucket persists themes and routing configuration uploaded from Ghost Admin. + +## Prepare the application + +You need [Git](https://git-scm.com/downloads), [jq](https://jqlang.org/download/), [s3cmd](https://s3tools.org/s3cmd) or any other S3-compatible client, and a [Clever Cloud account](https://console.clever-cloud.com/) to follow this guide. + +Create a directory and initialize its Git repository: + +```bash +mkdir myGhost +cd myGhost +git init +``` + +This guide targets Ghost `6.60.0`. To deploy another release, adapt `GHOST_VERSION` and use a Node.js version accepted by that release as described in the update section. + +[Mise is available on Clever Cloud](/developers/doc/reference/reference-environment-variables/#install-tools-with-mise-package-manager). Create the following `mise.toml` file to download the selected Ghost release, install its production dependencies and define the commands used by the Linux runtime: + +```toml {filename="mise.toml"} +[tools] +node = "{{ env.CC_NODE_VERSION }}" + +[tools."http:ghost"] +version = "{{ get_env(name='GHOST_VERSION', default='6.60.0') }}" +url = "https://registry.npmjs.org/ghost/-/ghost-{{ version }}.tgz" +strip_components = 1 + +[env] +database__client = "mysql" +database__connection__host = "{{ env.MYSQL_ADDON_HOST }}" +database__connection__port = "{{ env.MYSQL_ADDON_PORT }}" +database__connection__user = "{{ env.MYSQL_ADDON_USER }}" +database__connection__password = "{{ env.MYSQL_ADDON_PASSWORD }}" +database__connection__database = "{{ env.MYSQL_ADDON_DB }}" +logging__transports = '["stdout"]' +paths__contentPath = "{{ env.APP_HOME }}/content" +server__host = "0.0.0.0" +server__port = "{{ env.PORT }}" +storage__active = "S3Storage" +storage__S3Storage__bucket = "{{ env.GHOST_BUCKET }}" +storage__S3Storage__staticFileURLPrefix = "content/images" +storage__S3Storage__cdnUrl = "https://{{ env.CELLAR_ADDON_HOST }}/{{ env.GHOST_BUCKET }}" +storage__S3Storage__region = "default" +storage__S3Storage__endpoint = "https://{{ env.CELLAR_ADDON_HOST }}" +storage__S3Storage__forcePathStyle = "true" +storage__S3Storage__accessKeyId = "{{ env.CELLAR_ADDON_KEY_ID }}" +storage__S3Storage__secretAccessKey = "{{ env.CELLAR_ADDON_KEY_SECRET }}" + +# Multipart uploads start at 10 MiB; this threshold can be increased up to 5 GiB +# Chunks can be increased from 5 MiB up to 5 GiB, with at most 10,000 per upload +storage__S3Storage__multipartUploadThresholdBytes = "10485760" +storage__S3Storage__multipartChunkSizeBytes = "5242880" +storage__media__adapter = "S3Storage" +storage__media__staticFileURLPrefix = "content/media" +storage__files__adapter = "S3Storage" +storage__files__staticFileURLPrefix = "content/files" + +[tasks.build] +description = "Install Ghost and seed default content" +run = ''' +set -euo pipefail +mkdir ghost +cp -a '{{ tools["http:ghost"].path }}/.' ghost/ +mv ghost/content ghost-default-content +cd ghost +corepack enable pnpm +pnpm install --prod --frozen-lockfile +mkdir -p "$APP_HOME/content" +cp -a -n "$APP_HOME/ghost-default-content/." "$APP_HOME/content/" +''' + +[tasks.run] +description = "Start Ghost" +dir = "ghost" +run = "node index.js" +``` + +The [HTTP backend](https://mise.jdx.dev/dev-tools/backends/http.html) downloads and extracts the official npm package for the selected Ghost release. The `build` and `run` names are significant: the Linux runtime automatically uses these [Mise tasks](https://mise.jdx.dev/tasks/) for its separate build and run phases. At the end of a successful build, the build task copies only missing default content to persistent storage. Ghost logs to standard output so they are available through Clever Cloud's application logs. + +## Create the application and add-ons + +Install [Clever Tools](/developers/doc/cli/), log in and create a Linux application with an alias used to link the add-ons: + +```bash +npm i -g clever-tools +clever login + +clever create -t linux -a myGhost +``` + +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + +You can display your application's URL or add a custom domain. A custom domain also requires [DNS configuration](/developers/doc/administrate/domain-names/): + +```bash +clever domain +clever domain add your.website.tld +``` + +Create and link MySQL 8.4, Cellar and FS Bucket add-ons: + +```bash +clever addon create mysql-addon myGhostMySQL -p xxs_sml --addon-version 8.4 -l myGhost +clever addon create cellar-addon myGhostCellar -l myGhost +clever addon create fs-bucket myGhostContent -l myGhost +``` + +You can also create and link these resources from the [Clever Cloud Console](https://console.clever-cloud.com/). + +Mount the linked FS Bucket on the `content` directory. This path corresponds to `$APP_HOME/content` in the Mise configuration: + +```bash +FS_BUCKET_HOST="$(clever env -F json | jq -er 'first(.fromAddons[] | select(.addonName == "myGhostContent") | .env[] | select(.name == "BUCKET_HOST") | .value)')" +clever env set CC_FS_BUCKET "/content:${FS_BUCKET_HOST}" +unset FS_BUCKET_HOST +``` + +This name-based lookup is unambiguous only if the add-on name is unique. If several linked add-ons use the same name, it selects the first one found. To target the resource explicitly, replace the first command with the add-on ID returned by `clever addon create`: + +```bash +FS_BUCKET_HOST="$(clever addon env ADDON_ID -F json | jq -er '.BUCKET_HOST')" +``` + +## Configure Cellar + +Cellar bucket names are global within a cluster. Choose a unique name containing only lowercase letters, numbers, dots and hyphens: + +```bash +export GHOST_BUCKET="your-unique-ghost-bucket" +``` + +Export only the linked Cellar variables to your current shell, then create the bucket: + +```bash +source <(clever env -F shell | grep '^export CELLAR_ADDON_') + +s3cmd --access_key="$CELLAR_ADDON_KEY_ID" \ + --secret_key="$CELLAR_ADDON_KEY_SECRET" \ + --host="$CELLAR_ADDON_HOST" \ + --host-bucket="$CELLAR_ADDON_HOST" \ + --ssl mb "s3://$GHOST_BUCKET" +``` + +Ghost returns direct URLs for uploaded assets, so visitors need public read access to bucket objects. Create a policy that allows reads without allowing public writes or bucket listing: + +```bash +jq --null-input --arg bucket "$GHOST_BUCKET" '{ + Version: "2012-10-17", + Statement: [{ + Sid: "PublicRead", + Effect: "Allow", + Principal: "*", + Action: "s3:GetObject", + Resource: "arn:aws:s3:::\($bucket)/*" + }] +}' > ghost-bucket-policy.json + +s3cmd --access_key="$CELLAR_ADDON_KEY_ID" \ + --secret_key="$CELLAR_ADDON_KEY_SECRET" \ + --host="$CELLAR_ADDON_HOST" \ + --host-bucket="$CELLAR_ADDON_HOST" \ + --ssl setpolicy ghost-bucket-policy.json "s3://$GHOST_BUCKET" + +rm ghost-bucket-policy.json +unset CELLAR_ADDON_KEY_ID CELLAR_ADDON_KEY_SECRET CELLAR_ADDON_HOST +``` + +Keep `GHOST_BUCKET` exported for the next section. + +## Configure Ghost + +Store the application domain in a shell variable, using either its `.cleverapps.io` domain or your custom domain: + +```bash +export GHOST_URL="https://your-ghost-domain.example.com" +``` + +Configure the Ghost and Node.js versions, public URL and bucket name: + +```bash +clever env set GHOST_VERSION 6.60.0 +clever env set CC_NODE_VERSION 22.23.1 + +clever env set GHOST_BUCKET "$GHOST_BUCKET" +clever env set NODE_ENV production +clever env set url "$GHOST_URL" +``` + +The linked add-ons provide their credentials to the application. The `mise.toml` file maps them to Ghost's nested configuration and sets the server address and port. + +### Configure email + +Ghost requires transactional email for invitations, password resets, member sign-ins and Admin verification on new devices. Configure the SMTP service of your choice and replace every example value with your provider's settings: + +```bash +clever env set mail__transport SMTP +clever env set mail__from "Ghost " +clever env set mail__options__host smtp.example.com +clever env set mail__options__port 465 +clever env set mail__options__secure true +clever env set mail__options__auth__user SMTP_USERNAME +clever env set mail__options__auth__pass A_STRONG_SMTP_PASSWORD +``` + +See the [Ghost mail configuration](https://docs.ghost.org/config/#mail) for provider-specific options. Transactional email works with standard SMTP services, but Ghost's built-in bulk newsletter delivery [requires Mailgun](https://docs.ghost.org/newsletters/#bulk-email-configuration). + +If an SMTP service is not yet available during the initial configuration, you can temporarily disable Admin verification on new devices: + +```bash +clever env set security__staffDeviceVerification false +``` + +Only use this setting while configuring the application. Before using Ghost in production, configure SMTP and re-enable device verification: + +```bash +clever env set security__staffDeviceVerification true +clever restart +``` + +## Deploy Ghost + +Commit the deployment configuration and deploy the application: + +```bash +git add mise.toml +git commit -m "First deploy" + +clever deploy +``` + +Open the application and append `/ghost` to its URL to create the publication owner account: + +```bash +clever open +``` + +The public publication is available from the application URL, while Admin is available at `https://your-ghost-domain.example.com/ghost`. + +## Update Ghost + +Export your content from Ghost Admin and make sure you have a recent [MySQL backup](/developers/doc/cli/addons/#database-backups) before updating. Check the [`engines.node` requirement](https://github.com/TryGhost/Ghost/blob/v6.60.0/ghost/core/package.json) of the Ghost release you want to use, then update both version variables when necessary. For example: + +```bash +clever env set GHOST_VERSION 6.60.0 +clever env set CC_NODE_VERSION 22.23.1 +clever restart --without-cache +``` + +The rebuild downloads the selected release and installs dependencies for the configured Node.js version. Ghost applies pending MySQL migrations when the new version starts. + +## Learn more + +{{< cards >}} + {{< card link="https://docs.ghost.org/" title="Ghost documentation" subtitle="Configure and manage a Ghost publication" icon="ghost" >}} + {{< card link="/developers/doc/applications/linux" title="Linux application runtime" subtitle="Configure and deploy any application" icon="linux" >}} + {{< card link="/developers/doc/addons/mysql" title="MySQL" subtitle="Explore managed MySQL databases" icon="mysql" >}} + {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="Explore S3-compatible object storage" icon="cellar" >}} + {{< card link="/developers/doc/addons/fs-bucket" title="FS Bucket" subtitle="Persist files across deployments" icon="fsbucket" >}} +{{< /cards >}} diff --git a/data/icons.yaml b/data/icons.yaml index f8e3fc74e..308d742eb 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -97,6 +97,7 @@ clever: metabase: # Frameworks +ghost: Ghost meteor: svelte: maven: From 2580ca6299e59ff0fce4717f3a5775bbcfd712a7 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 11:43:37 +0200 Subject: [PATCH 127/214] guides(outline): replace startup script with Mise configuration --- content/guides/outline.md | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/content/guides/outline.md b/content/guides/outline.md index 1e4326c78..4e17dd356 100644 --- a/content/guides/outline.md +++ b/content/guides/outline.md @@ -41,6 +41,13 @@ version = "{{ get_env(name='OUTLINE_VERSION', default='v1.9.2') }}" url = "https://github.com/outline/outline/archive/refs/tags/{{ version }}.tar.gz" strip_components = 1 +[env] +DATABASE_URL = "{{ env.POSTGRESQL_ADDON_URI }}" +REDIS_COLLABORATION_URL = "{{ env.REDIS_URL }}" +AWS_ACCESS_KEY_ID = "{{ env.CELLAR_ADDON_KEY_ID }}" +AWS_SECRET_ACCESS_KEY = "{{ env.CELLAR_ADDON_KEY_SECRET }}" +AWS_S3_UPLOAD_BUCKET_URL = "https://{{ env.CELLAR_ADDON_HOST }}" + [tasks.build] description = "Build the selected Outline release" run = ''' @@ -57,28 +64,13 @@ yarn cache clean [tasks.run] description = "Start Outline" -run = "bash run.sh" +dir = "outline" +run = "node build/server/index.js" ``` -The [HTTP backend](https://mise.jdx.dev/dev-tools/backends/http.html) downloads and extracts the source archive. The `build` and `run` names are significant: the Linux runtime automatically uses these [Mise tasks](https://mise.jdx.dev/tasks/) for its separate build and run phases. - -Create a startup script that maps the variables injected by linked Clever Cloud add-ons to the names expected by Outline: - -```bash {filename="run.sh"} -#!/usr/bin/env bash -set -euo pipefail - -export DATABASE_URL="${POSTGRESQL_ADDON_URI}" -export REDIS_COLLABORATION_URL="${REDIS_URL}" -export AWS_ACCESS_KEY_ID="${CELLAR_ADDON_KEY_ID}" -export AWS_SECRET_ACCESS_KEY="${CELLAR_ADDON_KEY_SECRET}" -export AWS_S3_UPLOAD_BUCKET_URL="https://${CELLAR_ADDON_HOST}" - -cd outline -node build/server/index.js -``` +The [HTTP backend](https://mise.jdx.dev/dev-tools/backends/http.html) downloads and extracts the source archive. The `build` and `run` names are significant: the Linux runtime automatically uses these [Mise tasks](https://mise.jdx.dev/tasks/) for its separate build and run phases. The run task uses its [`dir` property](https://mise.jdx.dev/tasks/toml-tasks.html#task-directory) to start Outline from the extracted source directory. -The linked Redis add-on provides `REDIS_URL`, which the script also maps to `REDIS_COLLABORATION_URL` to synchronize collaborative editing between Outline processes and application instances. Outline automatically applies pending database migrations when it starts, so no separate migration command is needed. +The Mise environment maps the variables injected by linked Clever Cloud add-ons to the names expected by Outline. In particular, it maps `REDIS_URL` to `REDIS_COLLABORATION_URL` to synchronize collaborative editing between Outline processes and application instances. Outline automatically applies pending database migrations when it starts, so no separate migration command is needed. ## Create the application and add-ons @@ -199,7 +191,7 @@ clever env set AWS_S3_UPLOAD_BUCKET_NAME "$OUTLINE_BUCKET" Outline requires `SECRET_KEY` to contain exactly 64 hexadecimal characters, while `UTILS_SECRET` accepts the Base64 value. Clever Cloud provides the application `PORT`. The linked add-ons provide their credentials, so they do not need to be copied into the application environment. -`WEB_CONCURRENCY` controls the number of Outline processes started inside each application instance. Outline recommends roughly one process per 512 MB of available memory, so this guide keeps the value at `1` for the default `XS` run instance. Increase it only on a larger instance and after monitoring the application's memory and CPU usage. The `REDIS_COLLABORATION_URL` mapping in the startup script follows Outline's [horizontal scaling documentation](https://docs.getoutline.com/s/hosting/doc/horizontal-scaling-hkfU5Stao7), so collaborative editing remains synchronized if you later run several processes or application instances. +`WEB_CONCURRENCY` controls the number of Outline processes started inside each application instance. Outline recommends roughly one process per 512 MB of available memory, so this guide keeps the value at `1` for the default `XS` run instance. Increase it only on a larger instance and after monitoring the application's memory and CPU usage. The `REDIS_COLLABORATION_URL` mapping in `mise.toml` follows Outline's [horizontal scaling documentation](https://docs.getoutline.com/s/hosting/doc/horizontal-scaling-hkfU5Stao7), so collaborative editing remains synchronized if you later run several processes or application instances. `DEFAULT_LANGUAGE` controls the default interface language; replace `en_US` with another [Outline language code](https://translate.getoutline.com/) if needed. `FILE_STORAGE_UPLOAD_MAX_SIZE` is expressed in bytes: `262144000` sets a 250 MiB attachment limit and matches Outline's [versioned environment sample](https://github.com/outline/outline/blob/v1.9.2/.env.sample). You can configure a larger value, but Outline applies it to the [`content-length-range` condition of each presigned S3 upload](https://github.com/outline/outline/blob/v1.9.2/server/storage/files/S3Storage.ts#L44-L67), so test the intended size with your network and storage service before increasing it. Document and workspace imports can use separate limits; see Outline's [file storage documentation](https://docs.getoutline.com/s/hosting/doc/file-storage-N4M0T6Ypu7). @@ -238,7 +230,7 @@ Once signed in, users can also register passkeys for subsequent authentication. Commit the deployment configuration and deploy the application: ```bash -git add mise.toml run.sh +git add mise.toml git commit -m "First deploy" clever deploy From 3208e43d5581a8602bb18295dfbf3392084bb7eb Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 12:22:11 +0200 Subject: [PATCH 128/214] docs(contributing): document dependency version management --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1952ed81..8b1f2a732 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,6 +70,12 @@ Use the [Hextra callout shortcode](https://imfing.github.io/hextra/docs/guide/sh - Don't hard-wrap prose with formatting-only line breaks that don't affect rendering. Keep each paragraph on one logical line and rely on editor word wrap. - Sort lists and tables alphabetically unless a functional or chronological order is more useful +#### Dependencies and versions + +- All non-Docker runtimes provide a shared set of tools and version-management variables independently of the application's primary runtime. For example, `CC_NODE_VERSION` can select Node.js in a PHP application, while `CC_PHP_VERSION` can select PHP in a Node.js application. Other shared tools include Composer, Gradle, Hugo and Python. +- Prefer these dedicated environment variables and the package managers already provided by the runtime when they manage a dependency or its version. If a Mise task also needs the runtime-managed tool, derive its Mise version from the dedicated environment variable instead of hard-coding the version twice. +- For other deployment dependencies, prefer [Mise](https://mise.jdx.dev/) and a suitable backend such as [HTTP](https://mise.jdx.dev/dev-tools/backends/http.html) or [GitHub](https://mise.jdx.dev/dev-tools/backends/github.html) over custom download scripts, and pin versions when appropriate + ### 💅 Style guide - Don't override global styles for font type, size, or color From 973f54c6ea4dc999ae501a72678466c95edec885 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 12:50:24 +0200 Subject: [PATCH 129/214] develop(request-flow): document Docker limitation --- content/doc/develop/request-flow.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/doc/develop/request-flow.md b/content/doc/develop/request-flow.md index 34ef2f009..8d8c293f5 100644 --- a/content/doc/develop/request-flow.md +++ b/content/doc/develop/request-flow.md @@ -20,6 +20,8 @@ aliases: Request Flow is Clever Cloud's automatic middleware chaining mechanism. It configures reverse proxies and services between the public port (`8080`) and your application, managing port allocation automatically. There is no need to manually configure listening ports for each service. +Request Flow is available for all runtimes except Docker, where you define and manage services within the container image. + ## Supported services | Service | Activation | Description | From 8cec9db1826d83a6f2176b8125d047ed72de50dd Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 13:14:54 +0200 Subject: [PATCH 130/214] administrate(ssh): update immutable infrastructure reference --- content/doc/administrate/ssh-clever-tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/doc/administrate/ssh-clever-tools.md b/content/doc/administrate/ssh-clever-tools.md index 438a5dfd4..f463e1f2e 100644 --- a/content/doc/administrate/ssh-clever-tools.md +++ b/content/doc/administrate/ssh-clever-tools.md @@ -19,7 +19,7 @@ aliases: Clever Cloud allows you to connect to running instances via SSH. -While direct SSH access to instances is not recommended in an [immutable infrastructure](https://boxfuse.com/blog/no-ssh.html) setup, it can be useful for debugging purposes. +While direct SSH access to instances is not recommended in an [immutable infrastructure](https://glossary.cncf.io/immutable-infrastructure/) setup, it can be useful for debugging purposes. ## Purpose From e9f93487ea61358f4c10332b8cedd5cc2e6951c0 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 13:31:27 +0200 Subject: [PATCH 131/214] administrate(network): use API for outbound IP ranges --- content/doc/administrate/network.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/content/doc/administrate/network.md b/content/doc/administrate/network.md index 50590f26c..84c6de5c8 100644 --- a/content/doc/administrate/network.md +++ b/content/doc/administrate/network.md @@ -65,25 +65,19 @@ Two configurations are available: This is a custom, quote-based option. To discuss your requirements and pricing, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice) with your use case and target region. -## The "Paris" region +## PAR region (Paris) -The Paris region is owned and handled by Clever Cloud. We own or entrust the associated AS's and -IP addresses ranges. +The PAR region is owned and handled by Clever Cloud. We own or entrust the associated AS's and IP addresses ranges. The current outbound IP ranges are exposed by the public [Products zones API](/developers/api/v4/#products-zones). To list them for the Paris region: -Here are the current two addresses ranges your application may have an outgoing IP in: +```bash +curl -sS https://api.clever-cloud.com/v4/products/zones/par | jq -r '.outboundIPs[]' +``` -- 91.208.207.0/24 -- 185.133.116.0/22 +Clever Cloud may change these ranges at any moment while we expand our infrastructure. If filtering source IPs is important to you, query the API regularly. If you need a fixed outbound IP address or a VPN, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). -Clever Cloud may change these ranges at any moment while we expand our infrastructure. If -filtering source IPs is important to you, please check this page, or opt into our Unique -IP service or a VPN Service. - -Please note that allowing all ranges means you "allow" **all Clever Cloud -applications** running in the Paris region to access that service. -This means you should not base all that service security solely on filtering source IPs! +> [!WARNING] Shared IP ranges +> Allowing all ranges means allowing every Clever Cloud application running in the Paris region to access the service. Do not rely solely on source IP filtering to secure it. ## The other regions -The other regions we provide are hosted by other providers (OVHCloud, Scaleway, Oracle Cloud). -In this case, we use the IPs they provide to us and have no control over the ranges. +The other regions we provide are hosted by other providers, such as IONOS, OVHCloud, Scaleway. We use the IPs they provide to us and have no control over the ranges. From c35b82b51c055387cfaceceffc5996cdf4eb764e Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 14:32:09 +0200 Subject: [PATCH 132/214] chore: sync Vale --- .github/styles/Google/Anthropomorphism.yml | 12 ++++ .github/styles/Google/Colons.yml | 9 ++- .github/styles/Google/DateFormat.yml | 2 +- .github/styles/Google/ExcessiveClaims.yml | 14 +++++ .github/styles/Google/FirstPerson.yml | 10 ++-- .github/styles/Google/Headings.yml | 7 ++- .github/styles/Google/Jargon.yml | 13 +++++ .github/styles/Google/Latin.yml | 8 ++- .github/styles/Google/OxfordComma.yml | 23 +++++++- .github/styles/Google/Parens.yml | 10 +++- .github/styles/Google/Timeless.yml | 13 +++++ .github/styles/Google/Units.yml | 6 +- .github/styles/Google/WordList.yml | 57 +----------------- .github/styles/Google/WordListCase.yml | 68 ++++++++++++++++++++++ 14 files changed, 183 insertions(+), 69 deletions(-) create mode 100644 .github/styles/Google/Anthropomorphism.yml create mode 100644 .github/styles/Google/ExcessiveClaims.yml create mode 100644 .github/styles/Google/Jargon.yml create mode 100644 .github/styles/Google/Timeless.yml create mode 100644 .github/styles/Google/WordListCase.yml diff --git a/.github/styles/Google/Anthropomorphism.yml b/.github/styles/Google/Anthropomorphism.yml new file mode 100644 index 000000000..36137a166 --- /dev/null +++ b/.github/styles/Google/Anthropomorphism.yml @@ -0,0 +1,12 @@ +extends: existence +message: "Don't attribute human qualities to software or hardware ('%s')." +link: https://developers.google.com/style/anthropomorphism +level: suggestion +ignorecase: true +# Limited to the two verbs the guide itself names. Broader lists (wants, knows, +# thinks) can't tell a software subject from a human one: on a 950-file corpus +# they produced 8 false positives ('the customer wants', 'your audience knows') +# for every 2 real ones. +tokens: + - sees + - tells diff --git a/.github/styles/Google/Colons.yml b/.github/styles/Google/Colons.yml index 4a027c307..98972b987 100644 --- a/.github/styles/Google/Colons.yml +++ b/.github/styles/Google/Colons.yml @@ -1,8 +1,13 @@ extends: existence message: "'%s' should be in lowercase." link: 'https://developers.google.com/style/colons' -nonword: true level: warning scope: sentence +# The match is the word itself, not ': X', and `nonword` is off. Both are +# required for a project Vocab to work: Vale compares accept.txt entries +# against the matched text, and `nonword: true` opts out of that entirely. +# So a proper noun after a colon can be exempted by adding it to accept.txt. +# The guide's other exemption, notice labels, is handled by the lookbehinds; +# headings are already excluded by `scope: sentence`. See issue #20. tokens: - - '(? Date: Wed, 26 Aug 2026 14:32:29 +0200 Subject: [PATCH 133/214] chore: add Vale to project dependencies with Mise --- mise.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 mise.toml diff --git a/mise.toml b/mise.toml new file mode 100644 index 000000000..91cc5909a --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +vale = "latest" From 415ff5ade4846e26689df71f387d4ba19fb936c8 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 14:37:25 +0200 Subject: [PATCH 134/214] ci(external-doc): track Astro and Nuxt guide changes --- .github/external-doc-issue.yml | 11 +++--- .github/workflows/external-doc-update.yml | 42 ++++++----------------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/.github/external-doc-issue.yml b/.github/external-doc-issue.yml index b22601341..f4620b6d1 100644 --- a/.github/external-doc-issue.yml +++ b/.github/external-doc-issue.yml @@ -1,18 +1,17 @@ --- -title: External doc update from PR {{ env.PR_NUMBER }} -labels: documentation +title: Update external documentation after PR {{ env.PR_NUMBER }} +labels: enhancement --- ## External documentation update needed -Update the external documentation: +Review the corresponding external documentation after these changes: - Author: @{{ payload.sender.login }} - PR: {{ env.PR_URL }} - Modified files: {{ env.CHANGED_FILES }} -Make a PR on one of these repositories: +Open a pull request in the relevant repository: - [Astro](https://github.com/withastro/docs) - - +- [Nuxt](https://github.com/nuxt/nuxt) diff --git a/.github/workflows/external-doc-update.yml b/.github/workflows/external-doc-update.yml index 8038ed2fe..d0697dda5 100644 --- a/.github/workflows/external-doc-update.yml +++ b/.github/workflows/external-doc-update.yml @@ -1,40 +1,35 @@ -name: Update external doc? +name: Track external documentation updates on: pull_request: - types: [opened, synchronize] + types: [opened, reopened, synchronize] branches: [ main ] jobs: - changed_files: + external-documentation: runs-on: ubuntu-latest - name: Test changed-files + name: Check externally maintained guides environment: update doc - # Needed to post comments and create issues permissions: - issues: write - pull-requests: write - contents: read + contents: read + issues: write steps: - uses: actions/checkout@v4 - if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize' }} with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.ref }} token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 - # Check if any external doc needs an update - - name: Get files linked to external doc + - name: Find guides mirrored in external documentation id: changed-files-specific - uses: tj-actions/changed-files@v45 + uses: tj-actions/changed-files@v47 with: files: | content/guides/astro.md - context/guides/nuxt.md + content/guides/nuxt.md - # Create issue and assign PR author - - name: Create issue + - name: Create or update tracking issue id: create-issue if: steps.changed-files-specific.outputs.any_changed == 'true' env: @@ -48,20 +43,3 @@ jobs: assignees: ${{ github.event.pull_request.user.login }} update_existing: true search_existing: all - - # Post a single comment with auto-updating body - #- name: Create or update a comment - # env: - # CHANGED_FILES: ${{ steps.changed-files-specific.outputs.all_changed_files }} - # uses: taoliujun/action-unique-comment@v1 - # with: - # uniqueIdentifier: ${{ github.workflow }} - # body: | - # You updated ${{ env.CHANGED_FILES }}. This content is also listed on external doc. Issue number ${{ steps.create-issue.outputs.number }} has been created and assigned to you 🫵👁️👄👁️ - # - # See it or modify it here: - # * ${{ steps.create-issue.outputs.url }} - # - # _This unique comment uses the very cool [taoliujun/action-unique-comment](https://github.com/marketplace/actions/unique-comment). Thank you <3_ - - From ace1210068881d4cf00cbf0ee993088e0fcd0a0d Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 14:38:04 +0200 Subject: [PATCH 135/214] chore(issues): refine forms and labels --- .github/ISSUE_TEMPLATE/content-suggestion.yaml | 10 +++++----- .github/ISSUE_TEMPLATE/problem-report.yaml | 10 +++++----- .github/ISSUE_TEMPLATE/zzz-other.yaml | 5 ++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/content-suggestion.yaml b/.github/ISSUE_TEMPLATE/content-suggestion.yaml index f5199ec17..9a71ba68a 100644 --- a/.github/ISSUE_TEMPLATE/content-suggestion.yaml +++ b/.github/ISSUE_TEMPLATE/content-suggestion.yaml @@ -1,7 +1,7 @@ name: 📝 Content and guide suggestion -description: Suggest new documentation content, guides +description: Suggest new documentation content or a deployment guide title: "Content: " -labels: ["documentation", "enhancement"] +labels: ["enhancement"] body: - type: markdown attributes: @@ -16,9 +16,9 @@ body: description: "Have you checked if this content already exists?" options: - label: I have searched existing [documentation](https://www.clever.cloud/developers/) to verify this content doesn't already exist + required: true - label: I have checked for existing [issues](https://github.com/CleverCloud/documentation/issues) for this request - validations: - required: true + required: true - type: dropdown id: content-type @@ -53,7 +53,7 @@ body: id: framework-details attributes: label: "Technical details (for framework guides)" - description: "If it's a framework guide: name, version, specific challenges, etc." + description: "For a framework guide, provide its name, version and any deployment-specific requirements" placeholder: "Framework: Laravel 10, challenges: queue configuration with Redis, special dependencies..." - type: textarea diff --git a/.github/ISSUE_TEMPLATE/problem-report.yaml b/.github/ISSUE_TEMPLATE/problem-report.yaml index 82b5de0b4..84009ce4b 100644 --- a/.github/ISSUE_TEMPLATE/problem-report.yaml +++ b/.github/ISSUE_TEMPLATE/problem-report.yaml @@ -1,7 +1,7 @@ name: ⚠️ Report a problem description: Report an error, technical issue, UX problem, or request clarification title: "Issue: " -labels: ["documentation", "bug"] +labels: ["bug"] body: - type: markdown attributes: @@ -19,7 +19,7 @@ body: options: - Clarification request (unclear or incomplete information) - Documentation error (incorrect information, broken link, etc.) - - Technical site issue (Hugo build, shortcodes, functionality) + - Site or repository issue (Hugo, CI, shortcodes, rendering) - UX/design problem (accessibility, navigation, responsive) - Other problem validations: @@ -28,11 +28,11 @@ body: - type: textarea id: page-urls attributes: - label: "Affected page(s)" - description: "URLs of the documentation pages with the problem (one per line)" + label: "Affected page or file" + description: "Provide the affected documentation URL or repository path, one per line" placeholder: | https://www.clever.cloud/developers/... - https://www.clever.cloud/developers/... + .github/workflows/... validations: required: true diff --git a/.github/ISSUE_TEMPLATE/zzz-other.yaml b/.github/ISSUE_TEMPLATE/zzz-other.yaml index 1030139cc..a17e0be26 100644 --- a/.github/ISSUE_TEMPLATE/zzz-other.yaml +++ b/.github/ISSUE_TEMPLATE/zzz-other.yaml @@ -1,7 +1,6 @@ name: ❓ Other request description: For requests that don't fit into other categories title: "Other: " -labels: ["documentation"] body: - type: markdown attributes: @@ -19,9 +18,9 @@ body: description: "Have you checked the appropriate resources?" options: - label: I have checked that this request doesn't fit into other issue templates + required: true - label: I have searched for existing [issues](https://github.com/CleverCloud/documentation/issues) - validations: - required: true + required: true - type: textarea id: issue-description From 37914dcb233c39a7560328b80150df58f14243c1 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 14:38:40 +0200 Subject: [PATCH 136/214] chore(pull-request): simplify contribution template --- .github/pull_request_template.md | 43 ++++++-------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 478f756e9..9522ac4de 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,15 +1,12 @@ -## 📝 What does this PR do? +## 📝 Summary -_Briefly describe your changes and why they are needed_ +Describe the changes and their purpose - -## 🔗 Related Issue (if applicable) +## 🔗 Related issue - Closes # - Related to # ---- - ## 🧪 Type of Change - [ ] ⚠️ Bug fix @@ -18,33 +15,9 @@ _Briefly describe your changes and why they are needed_ - [ ] ✨ New content/feature - [ ] 🔧 Technical/maintenance ---- - -## ✅ Quick Checklist - -- [ ] I have read the [contributing guidelines](https://github.com/CleverCloud/documentation/blob/main/CONTRIBUTING.md) -- [ ] The content is accurate and links work -- [ ] The site builds without errors - ---- - -## 👥 Reviewers - -@CleverCloud/reviewers - ---- - -
-📋 For major changes (click to expand) - -### Additional testing performed -_Describe any specific testing done for complex changes_ - -### Screenshots -_Add screenshots for visual/layout changes_ - -### Breaking changes -_List any breaking changes or migration notes_ - -
+## ✅ Validation +- [ ] Read the [contributing guidelines](https://github.com/CleverCloud/documentation/blob/main/CONTRIBUTING.md) +- [ ] Verify content accuracy and links +- [ ] Run relevant checks and builds +- [ ] Check the rendered result when the change affects layout or formatting From a7434d3c8fe1fb4bd27448d06b4117a63a49b660 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 14:39:32 +0200 Subject: [PATCH 137/214] ci(vale): update action and support fork reviews --- .github/workflows/styles-review.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/styles-review.yml b/.github/workflows/styles-review.yml index 7e8a4c182..3f2558b28 100644 --- a/.github/workflows/styles-review.yml +++ b/.github/workflows/styles-review.yml @@ -5,11 +5,11 @@ jobs: vale: name: Run Vale Linter runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v4 - - uses: errata-ai/vale-action@reviewdog + - uses: vale-cli/vale-action@v3 with: - reporter: github-pr-check # How results are reported - filter_mode: added # Consider only changes in the modified or added lines - fail_on_error: false # Fail the action if errors are found - token: ${{ secrets.GITHUB_TOKEN }} # Post review + filter_mode: added + fail_on_error: false From d4fd13e90dce02fa051013c56d51eb63daff8303 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 14:45:03 +0200 Subject: [PATCH 138/214] chore(vale): normalize project vocabulary --- .../styles/config/vocabularies/Doc/accept.txt | 30 ++++++++----------- .../styles/config/vocabularies/Doc/reject.txt | 4 +-- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.github/styles/config/vocabularies/Doc/accept.txt b/.github/styles/config/vocabularies/Doc/accept.txt index 865e5b2e0..0d0a918c5 100644 --- a/.github/styles/config/vocabularies/Doc/accept.txt +++ b/.github/styles/config/vocabularies/Doc/accept.txt @@ -1,11 +1,10 @@ add-on -ant ANSI +Ant APIs -application app_id Astro -Astrowind +AstroWind Azimutt Blackfire boto @@ -18,7 +17,6 @@ charset checkMe Clever Cloud CLI -Cloud config Coraza Corepack @@ -31,20 +29,18 @@ Deno DNS Dockerfile downtimes -eg Entra EOL ESLint failover Filestash -Filezilla +FileZilla FPM FrankenPHP FSBucket Gemfile -Glassfish +GlassFish Gradle -gradle Grafana hardcoded healthcheck @@ -54,29 +50,29 @@ Hono hostname Infinispan jarName -Jboss +JBoss JSDoc Keycloak -Kubectl +kubectl Laravel Let's Encrypt LLM LLMs -Lume LTS +Lume Materia Matomo -maven +Maven Metabase middleware -monolog +Monolog monorepo monorepository Moodle namespace namespaces nano -nmap +Nmap npm Nuxt OAuth @@ -112,9 +108,9 @@ toolchain tooltip tooltips URIs -VSCode +VS Code WAF webroot -Wildfly -yjs +WildFly +Yjs Zooper diff --git a/.github/styles/config/vocabularies/Doc/reject.txt b/.github/styles/config/vocabularies/Doc/reject.txt index 2cbb58169..78b2a1572 100644 --- a/.github/styles/config/vocabularies/Doc/reject.txt +++ b/.github/styles/config/vocabularies/Doc/reject.txt @@ -1,6 +1,6 @@ addon addons -plug-in -plug-ins e-mail e-mails +plug-in +plug-ins From 538657847ea5a488bae59b1efcebd6d03e536ae8 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 14:46:05 +0200 Subject: [PATCH 139/214] ci(deploy): centralize Hugo version --- .github/workflows/deploy.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b3dfb7bd1..4cad6adbd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -11,6 +11,7 @@ concurrency: env: DOMAIN: ${{ github.event.pull_request.base.repo.name }}-PR-${{ github.event.number }}.cleverapps.io/developers + HUGO_VERSION: "0.164" jobs: review-app: @@ -35,7 +36,7 @@ jobs: CLEVER_SECRET: ${{ secrets.CLEVER_SECRET }} CLEVER_TOKEN: ${{ secrets.CLEVER_TOKEN }} ORGA_ID: ${{ secrets.ORGA_ID }} - GH_CC_HUGO_VERSION: 0.163 + GH_CC_HUGO_VERSION: ${{ env.HUGO_VERSION }} GH_CC_STATIC_AUTOBUILD_OUTDIR: public/developers GH_CC_WEBROOT: public GH_SERVER_ERROR_PAGE_404: developers/404.html From eb73dc5c8a5ad4b515d07ba209ca036671c65b94 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 15:06:45 +0200 Subject: [PATCH 140/214] docs(readme): correct setup and contribution guidance --- README.md | 228 ++++++++++++++++++++++++------------------------------ 1 file changed, 100 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 417b5b276..9fe6717a6 100644 --- a/README.md +++ b/README.md @@ -1,205 +1,177 @@ -# Clever Cloud Documentation +# Clever Cloud documentation -This project is based on [Hugo](https://gohugo.io/) and uses [Hextra](https://imfing.github.io/hextra/) theme. It provides a great design, a responsive layout, dark mode support, full-text search. It's lightweight, fast, and SEO-ready, with many built-in features: +The Clever Cloud documentation site uses [Hugo](https://gohugo.io/) and the [Hextra](https://imfing.github.io/hextra/) theme. It provides responsive navigation, dark mode, full-text search and the following content features: -- [Complete Markdown support](https://imfing.github.io/hextra/docs/guide/markdown/) -- [Complete shortcodes set](https://imfing.github.io/hextra/docs/guide/shortcodes/) - [Diagrams](https://imfing.github.io/hextra/docs/guide/diagrams/) -- [LaTeX math formulae](https://imfing.github.io/hextra/docs/guide/latex/) +- [LaTeX mathematical notation](https://imfing.github.io/hextra/docs/guide/latex/) +- [Markdown](https://imfing.github.io/hextra/docs/guide/markdown/) +- [Shortcode support](https://imfing.github.io/hextra/docs/guide/shortcodes/) - [Syntax highlighting](https://imfing.github.io/hextra/docs/guide/syntax-highlighting/) -## See deployed Documentation +## Published documentation -- [Home page](https://www.clever.cloud/developers/) -- [Documentation](https://www.clever.cloud/developers/doc/) -- [API Documentation](https://www.clever.cloud/developers/api/) -- [Guides and Tutorials](https://www.clever.cloud/developers/guides/) -- [Reference Clever Tools CLI](https://www.clever.cloud/developers/doc/reference/cli/) -- [Reference Environment Variables](https://www.clever.cloud/developers/doc/reference/reference-environment-variables/) -- [Clever Cloud Platform Changelog](https://www.clever.cloud/developers/changelog/) +- [Clever Cloud documentation](https://www.clever.cloud/developers/) +- [API reference](https://www.clever.cloud/developers/api/) +- [Clever Tools CLI reference](https://www.clever.cloud/developers/doc/reference/cli/) +- [Environment variables reference](https://www.clever.cloud/developers/doc/reference/reference-environment-variables/) +- [Guides and tutorials](https://www.clever.cloud/developers/guides/) +- [Platform changelog](https://www.clever.cloud/developers/changelog/) +- [Technical documentation](https://www.clever.cloud/developers/doc/) -## Quickstart +## Local development -### Install Hugo +### Requirements -To begin your journey with the Clever Cloud Documentation, you need Hugo. You can: -- [Install it](https://gohugo.io/installation/) globally on your system -- Get its [binary](https://github.com/gohugoio/hugo/releases) and put it in the project's root +Install the following tools before building the site: -### Clone the project, preview locally +- [Git](https://git-scm.com/downloads) +- [Go](https://go.dev/doc/install) 1.26 or later, which Hugo Modules uses to retrieve Hextra +- [Hugo Extended](https://gohugo.io/installation/) 0.154.5 or later -Once your system is set up, clone this repository: +### Preview the site + +Clone the repository and start Hugo's development server: ```bash -git clone https://github.com/CleverCloud/documentation +git clone https://github.com/CleverCloud/documentation.git +cd documentation +hugo server ``` -Then: - -1. Go to the documentation folder: `cd documentation` -2. Run `hugo server` to build and start the local server +The site is available at and refreshes when you modify a file. See the [`hugo server` documentation](https://gohugo.io/commands/hugo_server/) for the available options. -Local site is available on , it refreshes as you modify the files, you can keep the server running with no need to restart. -Find `server` command options in the [Hugo documentation](https://gohugo.io/commands/hugo_server/#options). +## Deploy on Clever Cloud -## Deploying on Clever Cloud - -The site is configured for Clever Cloud hosting with the `static` runtime and these required environment variables: +The site uses the Clever Cloud `static` runtime with the following environment variables: ```bash -# Declare what's the web server root, where to build the documentation -# You must have a `/developers` at the end of your application's route CC_WEBROOT="public" CC_STATIC_AUTOBUILD_OUTDIR="public/developers" - -# Declare the location of the 404 custom page SERVER_ERROR_PAGE_404="developers/404.html" ``` +Configure the application route so its path ends in `/developers`, which matches the site's configured base URL and output directory. + > [!TIP] -> You can set the Hugo version with `CC_HUGO_VERSION` with a value like `0.161` +> Set `CC_HUGO_VERSION` to a supported version such as `0.164` to select the Hugo version used for deployment -## Contributing +## Contribute -You can contribute by [creating an issue](https://github.com/CleverCloud/documentation/issues) or [submitting a pull request](https://github.com/CleverCloud/documentation/pulls). If you use AI tools or LLMs, you'll find specific instructions for them: +You can contribute by [creating an issue](https://github.com/CleverCloud/documentation/issues) or [submitting a pull request](https://github.com/CleverCloud/documentation/pulls). Read the following project instructions before submitting a change: -- [AI tools and LLMs instructions](./.github/copilot-instructions.md) -- [Claude Code guidance](./CLAUDE.md) +- [Coding agent instructions](./AGENTS.md) - [Contributing guidelines](./CONTRIBUTING.md) -Clever Cloud documentation is also available following the [llms.txt specification](https://www.clever.cloud/developers/llms.txt). +The documentation is also available in the [llms.txt format](https://www.clever.cloud/developers/llms.txt). -## Adding a new page or guide +## Add a page or guide -To generates a file from a template (in `/archetypes`), run one of the following Hugo commands: +Use the archetypes in [`archetypes/`](./archetypes/) to create new content. These examples create a guide, a documentation page and an application runtime page: ```bash -hugo new content guides/.md -hugo new content/doc/administrate/.md -hugo new content --kind applications doc/applications/.md +hugo new content --kind guides guides/my-framework.md +hugo new content doc/administrate/my-feature.md +hugo new content --kind applications doc/applications/my-runtime.md ``` -In new page/guide front matter, `draft` is set to `true` to prevent it from being mistakenly published. - -> [!TIP] -> Use `hugo server --buildDrafts` command to preview drafts locally - -### Adding a changelog entry (internal only) +Archetypes set `draft: true` so regular builds exclude unfinished pages. Preview drafts locally with: -For any significant change to the platform (updates, new features, etc.) a new entry is created in the `content/changelog` folder. - -Several entries can be made per day, it's not a problem. Each entry should provide clear, straightforward information on the essentials. If you find yourself writing an enormous amount of content, this may not be the right approach. However, you can always add a little charm to your changelog, but it's a tricky business, requiring careful, well-placed word choice. - -The filename format is a markdown file with a `.md` extension: - -``` -yyyy-mm-dd-your-title.md +```bash +hugo server --buildDrafts ``` -### Front matter configuration - -Hugo uses front matter to enrich posts with metadata. Front matter allows you to keep metadata attached to an instance of a content type—i.e., embedded inside a content file. We use the following Front matter variables: - -- [`type`](https://gohugo.io/methods/page/type/) (optional) - - The type of content layout to apply. The value is a ``, set it to `docs` except in changelog. - -- [`weight`](https://gohugo.io/methods/page/weight/) (optional) - - The weight of the content, used to order the sidebar. The value is an ``, default is `0`. +Remove the `draft` field when the page is ready to publish. -- [`linkTitle`](https://gohugo.io/methods/page/linktitle/) (optional) - - The title of the content displayed in the sidebar. The value is a ``, default is the `title` value. +### Add a changelog entry -- [`title`](https://gohugo.io/methods/page/title/) (required) - - The title displayed in the main heading. The value is a ``. +Clever Cloud team members add an entry to `content/changelog/` for each significant platform update. Keep entries concise and focused on information users need to understand or act on. -- [`description`](https://gohugo.io/methods/page/description/) (recommended) - - The description displayed in meta-description for SEO purposes. The value is a ``. +Store each entry under its publication year and name it with the `MM-DD-title.md` format, for example `content/changelog/2026/08-26-product-update.md`. Multiple entries can share the same date. -- [`excludeSearch`](https://imfing.github.io/hextra/docs/guide/configuration/#search-index) (optional) - - Indicates whether the page should be indexed in search. Default is `false`, we recommend setting it to `true` for changelog entries. +### Configure front matter -- [`aliases`](https://gohugo.io/methods/page/aliases) (optional) - - Aliases redirects the user to the right page. The value is a list of ``, each string being a path to redirect from, relative to the base URL (without the `/developer`, for example: `/doc/docker`). +Hugo front matter stores page metadata. This project uses the following fields: -- [`comments`](https://gohugo.io/content-management/comments/) (optional) - - Whether to show the feedback block or not. The value is a ``, default is `true`. +| Field | Status | Purpose | +| --- | --- | --- | +| `aliases` | Optional | Redirects paths that existed previously. Omit the `/developers` base path, for example `/doc/docker`, and don't add an alias to a new page | +| `authors` | Changelog | Lists contributors with `name`, `link` and `image` fields | +| `date` | Changelog | Sets the publication date or date-time in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format | +| `description` | Recommended | Provides the page's description for search results and search engines | +| `draft` | Optional | Excludes unfinished content from regular builds when set to `true`. Remove the field before publication | +| `excludeSearch` | Optional | Excludes the page from the search index when set to `true`. Set it for changelog entries | +| `keywords` | Optional | Lists search keywords | +| `linkTitle` | Optional | Sets a short sidebar label, usually the product name, and defaults to `title` | +| `tags` | Changelog | Lists lowercase product tags with consistent spelling | +| `title` | Required | Sets the page heading and search-oriented title. Avoid repeating the Clever Cloud name because the generated HTML title already includes it | +| `type` | Optional | Selects a content layout and uses `docs` outside the changelog | +| `weight` | Optional | Orders pages in the sidebar and defaults to `0` | -- [`draft`](https://gohugo.io/methods/page/draft/) (optional) - - Whether the page is a draft or not. The value is a ``, default is `false`. If set to `true`, the page is not built except if you use the `--buildDrafts` flag. - -- [`keywords`](https://gohugo.io/content-management/front-matter/#keywords) (optional) - - Keywords are used for SEO purposes. The value is a list of ``, each string being a keyword. - -- [`tags`](https://gohugo.io/content-management/front-matter/#taxonomies) (recommended) - - Tags are recommended only in Changelog for easy product identification. They are written in lowercase and, if possible, use the same spelling throughout the posts. The value is a list of ``. - -- [`authors`](https://gohugo.io/content-management/front-matter/#taxonomies) (mostly used in changelog) - - Can be set to showcase the people behind the product. Authors are defined with a `name`, `link` for their Github or any other social network, and an `image` for the profile picture. The profile picture can be set with the GitHub avatar with a link like `https://github.com/BlackYoup.png` and the parameter `?size=40` for reducing the image size (recommended for performance). The values are all of `` type. - -- [`date`](https://gohugo.io/methods/page/date/) (mostly used in changelog) - - The date that will be displayed in the post. The value is a string in ISO 8601 like `yyyy-mm-dd`. - -For example, a changelog entry front matter could look like this: +A changelog entry can use the following front matter: ```yaml --- -title: Redis updated to v7.2.4 -description: Redis has been updated to v7.2.4 mostly to prevent security issues -date: 2024-01-11 +title: Product 1.2 is available +description: Product 1.2 adds a new feature and fixes an upgrade issue +date: 2026-08-26 tags: - - redis + - product authors: - - name: BlackYoup - link: https://github.com/BlackYoup - image: https://github.com/BlackYoup.png?size=40 + - name: Clever Cloud + link: https://github.com/CleverCloud + image: https://github.com/CleverCloud.png?size=40 excludeSearch: true --- ``` -### Adding an image +See the [Hugo front matter documentation](https://gohugo.io/content-management/front-matter/) for the supported formats and built-in fields. + +### Add an image -Adding an image can be useful to highlight an interface change, for example. Use such markdown syntax for that: +Store documentation images under `static/images/` and reference them with Markdown: ```markdown -![Alt text](/images/your-image.jpg "Title of the image") +![Alt text](/images/your-image.jpg "Image title") ``` -If needed, you can also use [the `figure` shortcode](https://gohugo.io/shortcodes/figure/) to add attributes such as a width limit: +Use Hugo's [`figure` shortcode](https://gohugo.io/shortcodes/figure/) when the image needs attributes such as a width limit: ```markdown -{{< figure src="/developers/images/your-image.jpg" alt="Alt text" title="Title of the image" width="800px">}} +{{< figure src="/developers/images/your-image.jpg" alt="Alt text" title="Image title" width="800px">}} ``` -- [Learn more about Hugo shortcodes](https://gohugo.io/shortcodes/) +### Add shared content -### Adding a new shared content +Create reusable Markdown in [`shared/`](./shared/) and include it in a page with: -You can include shared content in several pages. To use this feature: - -1. Create a new markdown file in `/shared` -2. Add it to the relevant pages with: `{{% content "your-partial" %}}` +```markdown +{{% content "your-partial" %}} +``` > [!TIP] -> If you need to include a shared content including shortcodes, use `{{% content-raw "your-partial" %}}` instead. Don't include headings (starting with `#`) in it as they won't be rendered in the page Table of Contents (ToC). - -## Tooltips +> Use `{{% content-raw "your-partial" %}}` when the shared file contains shortcode markup. Don't add headings to shared files because they don't appear in the page table of contents -Tooltips are useful to provide additional information on terms or acronyms that may not be familiar to all readers. They help improve the accessibility and comprehension of your documentation without cluttering the main text. +### Add a tooltip -To create a tooltip, add the term and its associated tooltip definition in the [`data/tooltips.toml`](./data/tooltips.toml) file. Once defined, tooltips automatically display when users hover over associated terms in the documentation. +Add a term and its definition to [`data/tooltips.toml`](./data/tooltips.toml). The site then displays the definition when readers hover over matching terms. -## Markdown Linting +## Validate changes -Hugo uses [Goldmark](https://github.com/yuin/goldmark), a Markdown parser written in Go, compliant with [CommonMark 0.30](https://spec.commonmark.org/). Therefore, for better readability and maintainability, all markdown files for this project are linted with [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2). We strongly recommend that you follow the validation rules described [here](https://github.com/DavidAnson/markdownlint#rules--aliases). +Run a production build before submitting a change: -This linter can be downloaded and run locally, or used via Visual Studio Code: +```bash +hugo +``` -- for Visual Studio Code -- as a `npm` package +The repository provides [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) configuration in [`.markdownlint.jsonc`](./.markdownlint.jsonc) and exclusions in [`.markdownlintignore`](./.markdownlintignore). Run it with Node.js and `npx`: -**Ignored markdown files** are listed in the `.markdownlintignore`. -**Ignored specifications**, such as some HTML tag of Web Components, are configured in the `.markdownlint.jsonc` +```bash +npx markdownlint-cli2 "**/*.md" +``` -## Editorial checks +Editorial checks use [Vale](https://vale.sh/). The pull request workflow checks changed lines, and [Mise](https://mise.jdx.dev/) can install the configured Vale version and run the same styles locally: -This project uses [Vale.sh](https://vale.sh) to run editorial checks on the documentation. Install Vale on your machine or as an IDE extension if you want to run checks. This project is already configured to use it on pull requests. +```bash +mise install +mise exec -- vale README.md content shared +``` From d20f39c4e3485699766a7eb8eeb82fd4edb756c3 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 15:52:28 +0200 Subject: [PATCH 141/214] chore(mise): manage local development dependencies --- .github/workflows/deploy.yml | 1 + AGENTS.md | 14 ++++++++++---- CONTRIBUTING.md | 2 +- README.md | 18 ++++++++++-------- mise.toml | 3 +++ 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4cad6adbd..d8ac72ae4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -36,6 +36,7 @@ jobs: CLEVER_SECRET: ${{ secrets.CLEVER_SECRET }} CLEVER_TOKEN: ${{ secrets.CLEVER_TOKEN }} ORGA_ID: ${{ secrets.ORGA_ID }} + GH_CC_DISABLE_MISE: "true" GH_CC_HUGO_VERSION: ${{ env.HUGO_VERSION }} GH_CC_STATIC_AUTOBUILD_OUTDIR: public/developers GH_CC_WEBROOT: public diff --git a/AGENTS.md b/AGENTS.md index b56ac8c4c..d25a336c2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,7 @@ This file provides repository-wide guidance to coding agents working on Clever C ## Common Development Commands ### Hugo Site Development +- **Install development dependencies**: `mise install` - Installs the tools declared in `mise.toml` - **Local development**: `hugo server` - Serves site at http://localhost:1313 with live reload - **Build for production**: `hugo` - Outputs to `public/developers/` - **Preview drafts**: `hugo server --buildDrafts` - Include draft content in local preview @@ -72,9 +73,9 @@ For changelog entries, also include: - Time-dependent promises: "soon", "in the future", "coming next month" ### Markdown and Editorial Standards -- **Markdown linting**: Uses markdownlint-cli2 with config in `.markdownlint.jsonc` -- **Editorial checks**: Uses Vale.sh for style and terminology -- **Build verification**: Always test with `hugo` command before committing +- **Markdown linting**: Run `markdownlint-cli2 "**/*.md"` with config in `.markdownlint.jsonc` +- **Editorial checks**: Run Vale with `vale ` for style and terminology +- **Build verification**: Always test with `hugo` before committing - **Structure**: Use 2-4 well-developed paragraphs per section, minimize bullet lists - **Paragraphs**: Aim for 3-6 lines for optimal readability @@ -132,11 +133,16 @@ For changelog entries, also include: - Give `title` a descriptive, SEO-oriented value without repeating "Clever Cloud"; keep `linkTitle` short, usually the product name ## Deployment Configuration + The site is configured for Clever Cloud hosting with the `static` runtime and these required environment variables: + +- `CC_DISABLE_MISE="true"` - `CC_WEBROOT="public"` - `CC_STATIC_AUTOBUILD_OUTDIR="public/developers"` - `SERVER_ERROR_PAGE_404="developers/404.html"` -- Optional: `CC_HUGO_VERSION="0.161"` to specify Hugo version (example value) +- Optional: `CC_HUGO_VERSION="0.164"` to specify Hugo version (example value) + +`CC_DISABLE_MISE` prevents Clever Cloud from installing the local development dependencies because the platform manages the deployment tools directly. ## Data Management Runtime versions and software compatibility information is maintained in `/data/runtime_versions.yml` and should be kept current with platform capabilities. The site generates various output formats including standard HTML and a special LLMS output format at `/llms.txt` for AI consumption. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b1f2a732..b0fd3b86e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,7 @@ Use the [Hextra callout shortcode](https://imfing.github.io/hextra/docs/guide/sh ### ✅ Validation -Run `hugo` before submitting a change and fix any build error. Verify links, references, image paths, and shortcode syntax in the generated output. Inspect changed tabs, code blocks, cards, anchors, and copyable commands in the rendered HTML: a successful build alone doesn't prove that they render correctly. +Run `mise install` to install the development dependencies declared in `mise.toml`, then run `hugo` before submitting a change and fix any build error. Verify links, references, image paths, and shortcode syntax in the generated output. Inspect changed tabs, code blocks, cards, anchors, and copyable commands in the rendered HTML: a successful build alone doesn't prove that they render correctly. ## 🫶 Pull requests diff --git a/README.md b/README.md index 9fe6717a6..e9f06f113 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,9 @@ The Clever Cloud documentation site uses [Hugo](https://gohugo.io/) and the [Hex Install the following tools before building the site: - [Git](https://git-scm.com/downloads) -- [Go](https://go.dev/doc/install) 1.26 or later, which Hugo Modules uses to retrieve Hextra -- [Hugo Extended](https://gohugo.io/installation/) 0.154.5 or later +- [Mise](https://mise.jdx.dev/getting-started.html) + +Mise installs the Go, Hugo Extended, markdownlint-cli2 and Vale versions declared in [`mise.toml`](./mise.toml). ### Preview the site @@ -35,6 +36,7 @@ Clone the repository and start Hugo's development server: ```bash git clone https://github.com/CleverCloud/documentation.git cd documentation +mise install hugo server ``` @@ -45,12 +47,13 @@ The site is available at and refreshes when you modify a The site uses the Clever Cloud `static` runtime with the following environment variables: ```bash +CC_DISABLE_MISE="true" CC_WEBROOT="public" CC_STATIC_AUTOBUILD_OUTDIR="public/developers" SERVER_ERROR_PAGE_404="developers/404.html" ``` -Configure the application route so its path ends in `/developers`, which matches the site's configured base URL and output directory. +`CC_DISABLE_MISE` prevents Clever Cloud from installing the development dependencies because the static runtime provides and manages its deployment tools. Configure the route so its path ends in `/developers`, which matches the site's configured base URL and output directory. > [!TIP] > Set `CC_HUGO_VERSION` to a supported version such as `0.164` to select the Hugo version used for deployment @@ -163,15 +166,14 @@ Run a production build before submitting a change: hugo ``` -The repository provides [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) configuration in [`.markdownlint.jsonc`](./.markdownlint.jsonc) and exclusions in [`.markdownlintignore`](./.markdownlintignore). Run it with Node.js and `npx`: +The repository provides [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) configuration in [`.markdownlint.jsonc`](./.markdownlint.jsonc) and exclusions in [`.markdownlintignore`](./.markdownlintignore). Run the version installed by Mise: ```bash -npx markdownlint-cli2 "**/*.md" +markdownlint-cli2 "**/*.md" ``` -Editorial checks use [Vale](https://vale.sh/). The pull request workflow checks changed lines, and [Mise](https://mise.jdx.dev/) can install the configured Vale version and run the same styles locally: +Editorial checks use [Vale](https://vale.sh/). The pull request workflow checks changed lines, and Mise can run the same styles locally: ```bash -mise install -mise exec -- vale README.md content shared +vale README.md content shared ``` diff --git a/mise.toml b/mise.toml index 91cc5909a..2f2a3584e 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,5 @@ [tools] +go = "1.26" +hugo-extended = "0.164" +markdownlint-cli2 = "latest" vale = "latest" From 2feaeb4a51480063039e922240c3fe883c22e05b Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 16:54:15 +0200 Subject: [PATCH 142/214] docs: fix Markdown lint issues --- .markdownlint-cli2.jsonc | 3 + .markdownlint.jsonc | 25 +- .markdownlintignore | 2 +- AGENTS.md | 22 +- README.md | 30 +- archetypes/applications.md | 10 +- archetypes/guides.md | 6 +- content/api/howto.md | 94 +- content/api/v4.md | 106 +- .../2023/11-10-healthcheck-for-java.md | 4 +- content/changelog/2023/11-29-api-update.md | 2 +- .../12-13-cellar-migration-tool-update.md | 2 +- content/changelog/2023/12-15-sozu-0.15.18.md | 8 +- .../2024/01-10-documentation-changelog.md | 2 +- content/changelog/2024/01-11-redis-7.2.4.md | 8 +- content/changelog/2024/01-22-matomo-5.0.1.md | 1 + content/changelog/2024/01-24-ruby-3.3.0.md | 2 +- .../2024/01-25-clever-tools-3.1.0.md | 3 +- .../2024/02-07-clever-tools-3.2.0.md | 12 +- content/changelog/2024/02-08-sozu-0.15.19.md | 2 +- .../changelog/2024/02-12-new-grahds-region.md | 2 +- .../2024/02-16-clever-tools-3.4.0.md | 8 +- .../2024/02-22-postgresql-pgvector-support.md | 2 +- .../2024/02-26-healthcheck-for-everyone.md | 5 +- .../changelog/2024/02-26-mtl-iplb-update.md | 16 +- .../2024/02-27-images-update-part2.md | 12 +- .../2024/03-11-console-components-update.md | 2 +- content/changelog/2024/04-04-CVE-2024-3094.md | 10 +- .../2024/04-11-clever-tools-3.5.2.md | 14 +- .../04-15-new-logs-interface-public-beta.md | 4 +- .../2024/04-16-clever-tools-3.6.0.md | 2 +- .../2024/04-24-php-deprecate-warning.md | 2 +- content/changelog/2024/04-25-images-update.md | 22 +- .../changelog/2024/05-30-php-8.3-java-22.md | 28 +- content/changelog/2024/06-07-sozu-1.0.2.md | 4 +- .../2024/06-10-mongo-db-terraform.md | 2 +- .../2024/06-11-clever-tools-3.7.0.md | 2 +- .../changelog/2024/06-21-better-new-logs.md | 3 +- .../changelog/2024/06-28-new-ip-list-paris.md | 8 +- content/changelog/2024/07-01-matomo-5.1.0.md | 1 + .../2024/07-02-clever-tools-3.8.0.md | 3 +- .../changelog/2024/07-03-docker-terraform.md | 2 +- .../2024/07-04-apache-php-image-update.md | 9 +- .../2024/07-05-go-haskell-ruby-update.md | 14 +- .../2024/07-06-mysql-pgsql-update.md | 14 +- content/changelog/2024/07-10-CVE-2024-6387.md | 2 +- .../2024/07-17-keycloak-public-release.md | 2 +- .../changelog/2024/07-17-new-logs-default.md | 2 +- .../07-18-elastic-8.10.2-jenkins-2.452.3.md | 10 +- .../2024/07-18-github-action-review-apps.md | 2 +- .../changelog/2024/07-30-github-action-1.1.md | 2 +- .../2024/08-01-lagging-server-crate.md | 2 +- .../2024/08-09-python-3.12-update.md | 14 +- .../changelog/2024/08-30-deploy-url-mtl.md | 2 +- .../2024/09-12-clever-tools-3.8.3.md | 2 +- .../changelog/2024/09-12-js-client-9.0.0.md | 2 +- .../2024/10-01-python-image-changes.md | 4 +- .../changelog/2024/10-07-CVE-2024-47561.md | 2 +- content/changelog/2024/10-17-sozu-1.0.5.md | 2 +- .../2024/10-21-clever-tools-3.9.0.md | 3 +- .../2024/11-08-erlang-rust-update.md | 22 +- .../changelog/2024/11-08-mysql-mysql-8.4.0.md | 4 +- content/changelog/2024/11-09-java-update.md | 14 +- .../2024/11-21-eol-warnings-console.md | 1 + .../changelog/2024/11-27-clever-tools-3.10.md | 2 +- .../2024/12-12-python-3.13-update.md | 12 +- .../changelog/2024/12-18-clever-tools-3.11.md | 2 +- .../12-24-terraform-0.5-Keycloak-Metabase.md | 2 +- content/changelog/2025/01-06-sozu-1.0.6.md | 2 +- .../2025/01-15-node-22-lts-image-update.md | 2 +- content/changelog/2025/02-05-mysql-update.md | 4 +- .../changelog/2025/02-10-github-action-2.0.md | 2 +- .../changelog/2025/02-12-php-8.4-update.md | 12 +- .../2025/02-19-terraform-0.6-redis.md | 2 +- .../02-20-logs-interface-old-decomission.md | 2 +- content/changelog/2025/03-03-images-update.md | 38 +- .../changelog/2025/03-07-clever-tools-3.12.md | 6 +- content/changelog/2025/03-12-images-update.md | 28 +- .../changelog/2025/03-12-materia-kv-json.md | 1 + .../2025/03-18-images-update-ruby-unified.md | 24 +- content/changelog/2025/03-18-mysql-update.md | 6 +- .../changelog/2025/03-18-postgresql-16-17.md | 4 +- content/changelog/2025/03-19-paris-hds.md | 4 +- .../03-19-terraform-0.7-resources-update.md | 2 +- .../03-21-php-version-management-update.md | 6 +- .../2025/03-24-postgresql-11-12-eol.md | 9 +- .../changelog/2025/03-25-python-3.8-eol.md | 4 +- .../2025/03-27-postgresql-default.md | 4 +- .../2025/03-28-frankenphp-runtime.md | 6 +- content/changelog/2025/04-01-images-update.md | 14 +- .../2025/04-02-postgresql-replicas.md | 4 +- content/changelog/2025/04-03-images-update.md | 14 +- .../changelog/2025/04-04-postgresql-locale.md | 4 +- .../2025/04-10-mysql-8.4.4-update.md | 4 +- content/changelog/2025/04-16-images-update.md | 32 +- .../2025/04-24-keycloak-26.2.1-grafana.md | 1 + content/changelog/2025/05-06-images-update.md | 22 +- content/changelog/2025/05-07-node-24.md | 7 +- content/changelog/2025/05-13-images-update.md | 30 +- content/changelog/2025/05-16-images-update.md | 34 +- content/changelog/2025/05-16-terraform-0.8.md | 2 +- .../changelog/2025/05-20-pg-update-17.5.md | 13 +- content/changelog/2025/06-03-images-update.md | 40 +- .../2025/06-03-native-bun-support.md | 4 +- content/changelog/2025/06-03-terraform-0.9.md | 2 +- content/changelog/2025/06-04-otoroshi-17.3.md | 1 - .../2025/06-05-api-tokens-console-tips.md | 2 +- .../2025/06-05-mysql-8.0.42-8.4.5.md | 6 +- .../changelog/2025/06-10-clever-tools-3.13.md | 6 +- content/changelog/2025/06-17-images-update.md | 38 +- content/changelog/2025/07-01-images-update.md | 34 +- .../changelog/2025/07-02-terraform-0.10.md | 2 +- .../2025/07-04-linux-static-v-runtimes.md | 7 +- .../changelog/2025/07-08-terraform-0.11.md | 2 +- content/changelog/2025/07-09-images-update.md | 42 +- content/changelog/2025/07-16-otoroshi-17.4.md | 1 - content/changelog/2025/07-17-request-flow.md | 2 + .../changelog/2025/07-29-clever-tools-3.14.md | 2 +- content/changelog/2025/07-29-images-update.md | 76 +- content/changelog/2025/07-29-terraform-1.0.md | 2 +- content/changelog/2025/08-07-images-update.md | 28 +- content/changelog/2025/08-12-images-update.md | 28 +- .../changelog/2025/08-27-clever-tools-4.0.md | 3 +- .../2025/09-01-metabase-56-default.md | 1 + .../changelog/2025/09-01-pg-update-17.6.md | 13 +- content/changelog/2025/09-09-images-update.md | 60 +- content/changelog/2025/09-10-otoroshi-17.5.md | 1 - .../changelog/2025/09-12-terraform-1.1.0.md | 2 +- content/changelog/2025/09-16-images-update.md | 44 +- .../changelog/2025/09-17-clever-tools-4.1.md | 3 +- .../2025/09-18-mysql-8.0.43-8.4.6.md | 6 +- .../changelog/2025/09-24-terraform-1.2.0.md | 8 +- content/changelog/2025/09-30-images-update.md | 60 +- content/changelog/2025/10-07-images-update.md | 24 +- content/changelog/2025/10-07-redis-8.2.2.md | 1 + content/changelog/2025/10-08-postgresql-18.md | 4 +- content/changelog/2025/10-16-otoroshi-17.6.md | 1 - .../changelog/2025/10-21-clever-tools-4.2.md | 2 +- .../changelog/2025/10-22-clever-tools-4.3.md | 2 +- content/changelog/2025/10-23-images-update.md | 70 +- .../changelog/2025/10-27-terraform-1.3.0.md | 2 +- content/changelog/2025/10-31-otoroshi-17.7.md | 1 - content/changelog/2025/11-04-images-update.md | 46 +- .../changelog/2025/11-05-terraform-1.4.0.md | 2 +- content/changelog/2025/11-12-images-update.md | 56 +- .../changelog/2025/11-12-keycloak-26.4.4.md | 1 + .../changelog/2025/11-13-clever-tools-4.4.md | 2 +- .../changelog/2025/11-13-otoroshi-plugins.md | 2 + .../changelog/2025/11-17-terraform-1.5.0.md | 2 +- .../changelog/2025/11-19-terraform-1.6.0.md | 2 +- .../2025/12-01-metabase-57-default.md | 1 + .../changelog/2025/12-01-terraform-1.7.0.md | 2 +- content/changelog/2025/12-03-otoroshi-17.9.md | 1 + content/changelog/2025/12-15-pulsar-ga.md | 30 +- content/changelog/2025/12-17-images-update.md | 74 +- .../changelog/2025/12-19-terraform-1.8.0.md | 2 +- .../changelog/2026/01-14-clever-tools-4.5.md | 2 +- content/changelog/2026/01-15-images-update.md | 80 +- .../changelog/2026/01-23-terraform-1.9.0.md | 2 +- .../2026/01-27-metrics-retention-change.md | 4 +- content/changelog/2026/01-28-images-update.md | 57 +- content/changelog/2026/02-03-images-update.md | 48 +- content/changelog/2026/02-12-images-update.md | 39 +- .../changelog/2026/02-18-clever-tools-4.6.md | 2 +- content/changelog/2026/02-18-images-update.md | 34 +- content/changelog/2026/02-25-images-update.md | 26 +- .../changelog/2026/03-03-terraform-1.10.0.md | 2 +- .../changelog/2026/03-09-pg-update-18.3.md | 13 +- .../changelog/2026/03-11-clever-tools-4.7.md | 2 +- content/changelog/2026/03-19-drains-legacy.md | 3 +- .../2026/03-23-metabase-59-default.md | 1 + content/changelog/2026/03-25-images-update.md | 80 +- .../2026/03-31-mysql-8.0.45-8.4.8.md | 6 +- .../changelog/2026/04-02-terraform-1.11.0.md | 2 +- content/changelog/2026/04-07-images-update.md | 30 +- .../changelog/2026/04-09-clever-tools-4.8.md | 2 +- content/changelog/2026/04-21-images-update.md | 48 +- .../changelog/2026/04-23-otoroshi-17.15.md | 8 +- content/changelog/2026/04-28-images-update.md | 20 +- .../2026/05-11-materia-kv-graphql.md | 2 +- .../changelog/2026/05-21-pg-update-18.4.md | 13 +- content/changelog/2026/05-22-images-update.md | 88 +- .../changelog/2026/05-29-otoroshi-17.16.md | 12 +- content/changelog/2026/06-17-images-update.md | 66 +- .../changelog/2026/06-30-otoroshi-17.17.md | 8 +- content/changelog/2026/07-01-images-update.md | 28 +- content/changelog/2026/07-28-images-update.md | 71 +- .../2026/07-29-mysql-8.0.46-8.4.10.md | 6 +- content/changelog/2026/08-05-images-update.md | 50 +- content/changelog/2026/08-19-images-update.md | 50 +- content/doc/account/create-account.md | 4 +- content/doc/account/notifications.md | 26 +- content/doc/account/organisations.md | 22 +- content/doc/account/ssh-keys-management.md | 2 +- content/doc/addons/cellar.md | 102 +- content/doc/addons/config-provider.md | 20 +- content/doc/addons/elastic.md | 32 +- content/doc/addons/fs-bucket.md | 16 +- content/doc/addons/heptapod.md | 41 +- content/doc/addons/keycloak.md | 21 +- content/doc/addons/kms.md | 2 + content/doc/addons/materia-kv.md | 191 +-- content/doc/addons/materia-ts.md | 10 +- content/doc/addons/matomo.md | 2 +- content/doc/addons/metabase.md | 6 +- content/doc/addons/mysql.md | 1 + content/doc/addons/otoroshi.md | 11 +- content/doc/addons/postgresql.md | 126 +- content/doc/administrate/activity.md | 6 +- content/doc/administrate/clamav.md | 4 +- content/doc/administrate/domain-names.md | 95 +- content/doc/administrate/log-management.md | 14 +- content/doc/administrate/ssh-clever-tools.md | 15 +- content/doc/administrate/ssl.md | 16 +- content/doc/administrate/tcp-redirections.md | 2 +- content/doc/applications/docker.md | 3 +- content/doc/applications/dotnet.md | 6 +- content/doc/applications/frankenphp.md | 1 + content/doc/applications/golang.md | 22 +- content/doc/applications/haskell.md | 16 +- content/doc/applications/java/_index.md | 2 +- content/doc/applications/java/java-gradle.md | 2 +- content/doc/applications/java/java-jar.md | 2 +- content/doc/applications/java/java-maven.md | 16 +- content/doc/applications/java/java-war.md | 48 +- content/doc/applications/linux.md | 1 + content/doc/applications/meteor.md | 8 +- content/doc/applications/nodejs.md | 37 +- content/doc/applications/php/_index.md | 25 +- .../doc/applications/php/sessions-emails.md | 18 +- content/doc/applications/python/_index.md | 1 + content/doc/applications/python/servers.md | 40 +- content/doc/applications/python/uv.md | 12 +- content/doc/applications/rust.md | 21 +- .../applications/scala/play-framework-1.md | 24 +- .../applications/scala/play-framework-2.md | 12 +- content/doc/applications/static-apache.md | 3 + content/doc/applications/static.md | 2 + content/doc/applications/v.md | 1 + content/doc/best-practices/load-testing.md | 40 +- content/doc/best-practices/tips_and_tricks.md | 34 +- content/doc/billing/_index.md | 2 +- content/doc/billing/analytics-consumption.md | 8 +- content/doc/billing/payments-invoicing.md | 18 +- content/doc/billing/unified-invoices.md | 32 +- content/doc/ci-cd/custom-scripts.md | 4 +- content/doc/ci-cd/github.md | 4 +- content/doc/ci-cd/gitlab.md | 2 +- content/doc/cli/_index.md | 46 +- content/doc/cli/addons.md | 35 +- content/doc/cli/applications/_index.md | 26 +- content/doc/cli/applications/configuration.md | 34 +- .../cli/applications/deployment-lifecycle.md | 36 +- content/doc/cli/install/_index.md | 22 +- content/doc/cli/install/nexus-config.md | 42 +- content/doc/cli/kv-stores.md | 2 +- content/doc/cli/logs-drains.md | 10 +- content/doc/cli/network-groups.md | 36 +- content/doc/cli/notifications-webhooks.md | 18 +- content/doc/cli/operators.md | 21 +- content/doc/cli/profiles.md | 1 + content/doc/cli/services-depedencies.md | 14 +- content/doc/develop/env-variables.md | 92 +- content/doc/develop/network-groups.md | 13 +- content/doc/develop/oauth2-proxy.md | 12 +- content/doc/develop/request-flow.md | 30 +- content/doc/develop/tasks.md | 1 + content/doc/find-help/faq.md | 35 +- content/doc/find-help/support.md | 4 +- content/doc/find-help/troubleshooting.md | 8 +- content/doc/functions/_index.md | 4 +- content/doc/kubernetes/_index.md | 74 +- content/doc/kubernetes/operator.md | 36 +- content/doc/marketplace.md | 96 +- content/doc/metrics/_index.md | 128 +- content/doc/metrics/new-relic.md | 14 +- content/doc/metrics/warp10.md | 3 +- content/doc/quickstart.md | 50 +- content/doc/reference/cli.md | 1379 +++++++++++------ content/doc/reference/products-support.md | 4 +- .../reference-environment-variables.md | 826 +++++----- content/guides/astro.md | 5 +- content/guides/docs.md | 16 +- content/guides/docusaurus.md | 2 +- content/guides/ekg-statsd-haskell-metrics.md | 8 +- content/guides/eleventy.md | 2 +- content/guides/fluentd.md | 22 +- content/guides/ghost.md | 2 +- content/guides/go-echoip.md | 26 +- content/guides/hexo.md | 3 +- content/guides/hono.md | 4 +- content/guides/hugo-static-s3.md | 2 +- content/guides/hugo.md | 2 +- content/guides/kibana.md | 14 +- content/guides/lume-deno.md | 3 +- content/guides/mdbook.md | 2 +- content/guides/mkdocs.md | 3 +- content/guides/moodle.md | 3 +- content/guides/node-js-mongo-db.md | 2 +- content/guides/nuxt.md | 4 +- content/guides/outline.md | 18 +- content/guides/pgpool.md | 2 +- content/guides/ruby-rack-app-tutorial.md | 8 +- content/guides/ruby-rack.md | 8 +- content/guides/sftpgo.md | 20 +- content/guides/sveltekit.md | 2 +- content/guides/tutorial-drupal.md | 18 +- content/guides/tutorial-laravel.md | 2 +- content/guides/tutorial-symfony.md | 6 +- content/guides/tutorial-wordpress.md | 2 +- content/postmortem/2024-08-02.md | 78 +- content/postmortem/2025-03-03.md | 220 +-- content/postmortem/2025-10-09.md | 86 +- content/postmortem/2026-03-19.md | 32 +- drafts/community.md | 6 +- shared/kv-explorer.md | 2 +- shared/redirectionio.md | 10 +- shared/ruby.md | 42 +- shared/warp10-concepts.md | 4 +- shared/warp10-content.md | 30 +- update-cli-reference.sh | 2 + 321 files changed, 4243 insertions(+), 3643 deletions(-) create mode 100644 .markdownlint-cli2.jsonc diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 000000000..3fe7bdc40 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,3 @@ +{ + "gitignore": true +} diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc index 22de7da50..25a9920da 100644 --- a/.markdownlint.jsonc +++ b/.markdownlint.jsonc @@ -1,16 +1,23 @@ { - "MD041": false, - "MD013": false, - "MD051": false, - "MD029": false, - // MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md033.md - "MD033": { + "MD004": { + "style": "dash" + }, + "MD013": false, + "MD029": false, + // MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md033.md + "MD033": { // Allowed elements "allowed_elements": [ "br", - "cc-smart-container", - "cc-pricing-product", "cite" ] + }, + "MD041": false, + "MD051": false, + "MD055": { + "style": "leading_and_trailing" + }, + "MD060": { + "style": "aligned" } -} \ No newline at end of file +} diff --git a/.markdownlintignore b/.markdownlintignore index ab4b6d49a..a6cb7ebc9 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -1,2 +1,2 @@ -/public/* +/public/** /resources diff --git a/AGENTS.md b/AGENTS.md index d25a336c2..1a2b8ee7b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,13 +5,15 @@ This file provides repository-wide guidance to coding agents working on Clever C ## Common Development Commands ### Hugo Site Development + - **Install development dependencies**: `mise install` - Installs the tools declared in `mise.toml` -- **Local development**: `hugo server` - Serves site at http://localhost:1313 with live reload +- **Local development**: `hugo server` - Serves site at with live reload - **Build for production**: `hugo` - Outputs to `public/developers/` - **Preview drafts**: `hugo server --buildDrafts` - Include draft content in local preview - **Update CLI reference**: `./update-cli-reference.sh` - Fetches latest clever-tools documentation ### Content Generation + - **New guide**: `hugo new content guides/.md` - **New documentation**: `hugo new content/doc/administrate/.md` - **New application runtime**: `hugo new content --kind applications doc/applications/.md` @@ -19,6 +21,7 @@ This file provides repository-wide guidance to coding agents working on Clever C ## Project Architecture ### Content Organization + This is a Hugo-based documentation site using the Hextra theme with the following structure: - **`/content/`** - All documentation content: @@ -36,7 +39,9 @@ This is a Hugo-based documentation site using the Hextra theme with the followin - **`/layouts/`** - Hugo templates and shortcodes for content rendering ### Content Types and Front Matter + Content uses Hugo front matter with fields such as: + - `type: docs` - Content layout type - `weight` - Sidebar ordering (integer) - `linkTitle` - Short title for sidebar navigation @@ -47,11 +52,13 @@ Content uses Hugo front matter with fields such as: - `excludeSearch: true` - Excludes from search index (recommended for changelog) For changelog entries, also include: + - `date: YYYY-MM-DD` - Publication date - `tags` - Array of product tags (lowercase) - `authors` - Array with `name`, `link`, `image` fields ### Shared Content System + - Include shared content: `{{% content "filename" %}}` - Include shared content with shortcodes: `{{% content-raw "filename" %}}` - Shared files should not contain headings (breaks ToC generation) @@ -59,6 +66,7 @@ For changelog entries, also include: ## Quality Standards ### Content Quality Requirements + - Use second person ("you") addressing readers directly - Write in active voice, avoid passive constructions - Use `organisation` and `organisations`, rather than `organization` and `organizations`; this isn't a general British-spelling requirement @@ -67,12 +75,14 @@ For changelog entries, also include: - Explain prerequisites and non-obvious behaviour, while keeping the main task path concise ### Prohibited Elements + - First-person pronouns: I, me, my, we, us, our, let's - Placeholder phrases: "please note", "at this time", "it should be noted" - Overconfident claims: "simply", "just", "easily", "quickly", "obviously" - Time-dependent promises: "soon", "in the future", "coming next month" ### Markdown and Editorial Standards + - **Markdown linting**: Run `markdownlint-cli2 "**/*.md"` with config in `.markdownlint.jsonc` - **Editorial checks**: Run Vale with `vale ` for style and terminology - **Build verification**: Always test with `hugo` before committing @@ -80,7 +90,9 @@ For changelog entries, also include: - **Paragraphs**: Aim for 3-6 lines for optimal readability ### Callouts + - Prefer GitHub-style callouts with a concise title on the marker line, as supported by the theme: + ```markdown > [!NOTE] Current behaviour > This information helps readers understand the current behaviour @@ -88,10 +100,12 @@ For changelog entries, also include: > [!WARNING] Back up your data > Back up your application database before upgrading ``` + - Use the Hugo `{{< callout >}}` shortcode only when GitHub-style syntax can't provide the required rendering or behaviour - Limit callouts to one or two per page ### Commit Messages + - For content updates, use `section(page): commit message`, for example: - `addons(postgresql): document pg_partman support` - `applications(nodejs): clarify pnpm configuration` @@ -106,6 +120,7 @@ For changelog entries, also include: - Start the subject with a lowercase imperative verb ### Code and Technical Examples + - Always provide complete, runnable code examples - Keep commands literally copyable: don't put shell-invalid placeholders or bracketed optional arguments in executable code blocks - Show optional flags in separate examples or explain where to add them @@ -145,11 +160,13 @@ The site is configured for Clever Cloud hosting with the `static` runtime and th `CC_DISABLE_MISE` prevents Clever Cloud from installing the local development dependencies because the platform manages the deployment tools directly. ## Data Management + Runtime versions and software compatibility information is maintained in `/data/runtime_versions.yml` and should be kept current with platform capabilities. The site generates various output formats including standard HTML and a special LLMS output format at `/llms.txt` for AI consumption. ## Hugo Shortcodes and Features ### Content Shortcodes + - `{{% content "filename" %}}` - Include shared content from `/shared/` directory - `{{% content-raw "filename" %}}` - Include shared content containing shortcodes - `{{% steps %}}` - Create step-by-step instructions for guides @@ -159,12 +176,14 @@ Runtime versions and software compatibility information is maintained in `/data/ - `{{< hextra/hero-subtitle >}}` - Add engaging subtitles in guides ### Hugo Content Types + - **Documentation pages**: Use `type: docs` in front matter - **Guides**: Use step-by-step structure, hero subtitles, and cards when they improve the guide - **Changelog entries**: Include date, tags, and author information - **API documentation**: Structured reference content ### Hextra Theme Features + - **Search**: Full-text search using FlexSearch - **Dark mode**: Automatic theme switching - **Responsive navigation**: Sidebar and mobile-friendly menus @@ -172,4 +191,5 @@ Runtime versions and software compatibility information is maintained in `/data/ - **Syntax highlighting**: Code block highlighting with copy functionality ## File Standards + All text files must end with a newline. diff --git a/README.md b/README.md index e9f06f113..1f5f26cf5 100644 --- a/README.md +++ b/README.md @@ -95,20 +95,20 @@ Store each entry under its publication year and name it with the `MM-DD-title.md Hugo front matter stores page metadata. This project uses the following fields: -| Field | Status | Purpose | -| --- | --- | --- | -| `aliases` | Optional | Redirects paths that existed previously. Omit the `/developers` base path, for example `/doc/docker`, and don't add an alias to a new page | -| `authors` | Changelog | Lists contributors with `name`, `link` and `image` fields | -| `date` | Changelog | Sets the publication date or date-time in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format | -| `description` | Recommended | Provides the page's description for search results and search engines | -| `draft` | Optional | Excludes unfinished content from regular builds when set to `true`. Remove the field before publication | -| `excludeSearch` | Optional | Excludes the page from the search index when set to `true`. Set it for changelog entries | -| `keywords` | Optional | Lists search keywords | -| `linkTitle` | Optional | Sets a short sidebar label, usually the product name, and defaults to `title` | -| `tags` | Changelog | Lists lowercase product tags with consistent spelling | -| `title` | Required | Sets the page heading and search-oriented title. Avoid repeating the Clever Cloud name because the generated HTML title already includes it | -| `type` | Optional | Selects a content layout and uses `docs` outside the changelog | -| `weight` | Optional | Orders pages in the sidebar and defaults to `0` | +| Field | Status | Purpose | +| --------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| `aliases` | Optional | Redirects paths that existed previously. Omit the `/developers` base path, for example `/doc/docker`, and don't add an alias to a new page | +| `authors` | Changelog | Lists contributors with `name`, `link` and `image` fields | +| `date` | Changelog | Sets the publication date or date-time in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format | +| `description` | Recommended | Provides the page's description for search results and search engines | +| `draft` | Optional | Excludes unfinished content from regular builds when set to `true`. Remove the field before publication | +| `excludeSearch` | Optional | Excludes the page from the search index when set to `true`. Set it for changelog entries | +| `keywords` | Optional | Lists search keywords | +| `linkTitle` | Optional | Sets a short sidebar label, usually the product name, and defaults to `title` | +| `tags` | Changelog | Lists lowercase product tags with consistent spelling | +| `title` | Required | Sets the page heading and search-oriented title. Avoid repeating the Clever Cloud name because the generated HTML title already includes it | +| `type` | Optional | Selects a content layout and uses `docs` outside the changelog | +| `weight` | Optional | Orders pages in the sidebar and defaults to `0` | A changelog entry can use the following front matter: @@ -166,7 +166,7 @@ Run a production build before submitting a change: hugo ``` -The repository provides [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) configuration in [`.markdownlint.jsonc`](./.markdownlint.jsonc) and exclusions in [`.markdownlintignore`](./.markdownlintignore). Run the version installed by Mise: +The repository provides [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) rules in [`.markdownlint.jsonc`](./.markdownlint.jsonc), CLI configuration in [`.markdownlint-cli2.jsonc`](./.markdownlint-cli2.jsonc) and editor exclusions in [`.markdownlintignore`](./.markdownlintignore). Run the version installed by Mise: ```bash markdownlint-cli2 "**/*.md" diff --git a/archetypes/applications.md b/archetypes/applications.md index edaf1e36e..75fc50b4e 100644 --- a/archetypes/applications.md +++ b/archetypes/applications.md @@ -16,8 +16,9 @@ To create a new XXX application, use the [Clever Cloud Console](https://console. ```bash clever create --type XXX ``` -* [Learn more about Clever Tools](/doc/cli/) -* [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) + +- [Learn more about Clever Tools](/doc/cli/) +- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) ## Configure your XXX application @@ -25,7 +26,7 @@ clever create --type XXX XXX runtime only requires a working application listening on `0.0.0.0:8080`. -* [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) +- [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) ### Build phase @@ -35,11 +36,8 @@ During the build phase, ### XXX version - - ## Clever Tasks - ```bash clever create --type XXX --task "XXX" clever deploy # or clever restart if there is no code change diff --git a/archetypes/guides.md b/archetypes/guides.md index 9cc1ecef5..7012332b6 100644 --- a/archetypes/guides.md +++ b/archetypes/guides.md @@ -18,9 +18,9 @@ draft: true {{% steps %}} -### +### First step -### +### Second step {{% /steps %}} @@ -28,4 +28,4 @@ draft: true {{< cards >}} {{< card link="/developers/guides/" title="Card title" subtitle="Card subtiltle" icon="adjustments-horizontal" >}} -{{< /cards >}} \ No newline at end of file +{{< /cards >}} diff --git a/content/api/howto.md b/content/api/howto.md index 99c6b544f..3c3ca229b 100644 --- a/content/api/howto.md +++ b/content/api/howto.md @@ -24,9 +24,9 @@ The [Clever Cloud Console](https://console.clever-cloud.com) and [Clever Tools]( Clever Cloud's REST API offers two authentication mechanisms to meet different integration needs: -* **API tokens** provide a straightforward way to authenticate requests on behalf of a specific user. These tokens operate similarly to passwords and should be handled with appropriate security measures. API tokens are ideal for personal scripts, CLI tools, and scenarios where you're accessing your own resources. Use them to request the API Bridge: https://api-bridge.clever-cloud.com +- **API tokens** provide a straightforward way to authenticate requests on behalf of a specific user. These tokens operate similarly to passwords and should be handled with appropriate security measures. API tokens are ideal for personal scripts, CLI tools, and scenarios where you're accessing your own resources. Use them to request the API Bridge: -* **OAuth 1** is designed for third-party applications that need to access Clever Cloud resources on behalf of their users. This authentication flow allows applications to request permissions from users without requiring direct access to their credentials. OAuth 1 is recommended for public applications, services that integrate with multiple user accounts, or any scenario where user delegation is required. +- **OAuth 1** is designed for third-party applications that need to access Clever Cloud resources on behalf of their users. This authentication flow allows applications to request permissions from users without requiring direct access to their credentials. OAuth 1 is recommended for public applications, services that integrate with multiple user accounts, or any scenario where user delegation is required. Choose the authentication method that best aligns with your specific integration requirements and security considerations. @@ -72,6 +72,7 @@ clever curl https://api.clever-cloud.com/v4/billing/organisations/ [!TIP] > For quick testing, you can use `PLAINTEXT` with curl (percent-encode your consumer secret if it contains non-alphanumeric characters): +> > ```bash > curl -X POST "https://api.clever-cloud.com/v2/oauth/request_token_query?\ > oauth_consumer_key=&oauth_signature_method=PLAINTEXT&\ @@ -180,48 +182,48 @@ https://api.clever-cloud.com/v2/oauth/authorize?oauth_token= The user logs into Clever Cloud (if not already) and is presented with a permissions form. Available permissions are: -| Permission | Description | -|---|---| -| `access_organisations` | Access organisations | -| `access_organisations_bills` | Access organisations' bills | +| Permission | Description | +| --------------------------------------------- | -------------------------------------------- | +| `access_organisations` | Access organisations | +| `access_organisations_bills` | Access organisations' bills | | `access_organisations_consumption_statistics` | Access organisations' consumption statistics | -| `access_organisations_credit_count` | Access organisations' credit count | -| `access_personal_information` | Access personal information | -| `manage_organisations` | Manage organisations | -| `manage_organisations_applications` | Manage organisations' applications | -| `manage_organisations_members` | Manage organisations' members | -| `manage_organisations_services` | Manage organisations' add-ons | -| `manage_personal_information` | Manage personal information | -| `manage_ssh_keys` | Manage SSH keys | +| `access_organisations_credit_count` | Access organisations' credit count | +| `access_personal_information` | Access personal information | +| `manage_organisations` | Manage organisations | +| `manage_organisations_applications` | Manage organisations' applications | +| `manage_organisations_members` | Manage organisations' members | +| `manage_organisations_services` | Manage organisations' add-ons | +| `manage_personal_information` | Manage personal information | +| `manage_ssh_keys` | Manage SSH keys | You can retrieve this list programmatically with `GET https://api.clever-cloud.com/v2/oauth/rights`. Once the user approves, the browser is redirected to your `oauth_callback` URL with the following **query string** parameters: -| Parameter | Description | -|---|---| -| `oauth_token` | The request token (must match the one from step 1) | -| `oauth_verifier` | Verification code to exchange for an access token | +| Parameter | Description | +| ---------------- | -------------------------------------------------- | +| `oauth_token` | The request token (must match the one from step 1) | +| `oauth_verifier` | Verification code to exchange for an access token | ##### Exchange for an access token Exchange the request token and verifier for an access token. As with step 1, parameters can be sent as query string or form body: -* `POST https://api.clever-cloud.com/v2/oauth/access_token_query` — parameters as query string -* `POST https://api.clever-cloud.com/v2/oauth/access_token` — parameters as `application/x-www-form-urlencoded` body +- `POST https://api.clever-cloud.com/v2/oauth/access_token_query` — parameters as query string +- `POST https://api.clever-cloud.com/v2/oauth/access_token` — parameters as `application/x-www-form-urlencoded` body **Required parameters:** -| Parameter | Description | -|---|---| -| `oauth_consumer_key` | Your consumer key | -| `oauth_signature_method` | Same method as step 1 | -| `oauth_signature` | Request signature (signed with the request token secret from step 1) | -| `oauth_timestamp` | Current Unix timestamp (seconds) | -| `oauth_nonce` | Unique random string | -| `oauth_version` | `1.0` | -| `oauth_token` | The request token from step 1 | -| `oauth_verifier` | The verifier from the callback redirect | +| Parameter | Description | +| ------------------------ | -------------------------------------------------------------------- | +| `oauth_consumer_key` | Your consumer key | +| `oauth_signature_method` | Same method as step 1 | +| `oauth_signature` | Request signature (signed with the request token secret from step 1) | +| `oauth_timestamp` | Current Unix timestamp (seconds) | +| `oauth_nonce` | Unique random string | +| `oauth_version` | `1.0` | +| `oauth_token` | The request token from step 1 | +| `oauth_verifier` | The verifier from the callback redirect | **Response** (`application/x-www-form-urlencoded`) @@ -316,9 +318,9 @@ function buildBaseString(method, url, params) { The three components are: -* The HTTP method in uppercase (`GET`, `POST`, etc.) -* The base URL (without query string), percent-encoded -* All request parameters (OAuth parameters excluding `oauth_signature`, plus any query string or form body parameters), sorted alphabetically by key — then by value in case of duplicates — formatted as `key=value` pairs joined by `&`, then percent-encoded as a single string +- The HTTP method in uppercase (`GET`, `POST`, etc.) +- The base URL (without query string), percent-encoded +- All request parameters (OAuth parameters excluding `oauth_signature`, plus any query string or form body parameters), sorted alphabetically by key — then by value in case of duplicates — formatted as `key=value` pairs joined by `&`, then percent-encoded as a single string **3.** Define `sign` — it computes an HMAC with the chosen algorithm and returns the base64-encoded result: diff --git a/content/api/v4.md b/content/api/v4.md index 93f659c60..22ff8dc6a 100644 --- a/content/api/v4.md +++ b/content/api/v4.md @@ -13,7 +13,8 @@ keywords: --- Clever Cloud provides a public REST API used by its own services and customers, known as [`/v2`](/api/v2/). As our platform evolves, we're progressively moving to `/v4` endpoints, adding functionalities, with a more modern approach: -``` + +```text https://api.clever-cloud.com/v4/ ``` @@ -24,6 +25,7 @@ Each query response contains a `Sozu-Id`. If you need help from our support team {{< /callout>}} ## Billing + - `/v4/billing/organisations/{orgid}/invoices` - `/v4/billing/organisations/{orgid}/invoices/{invoiceid}` - `/v4/billing/organisations/{orgid}/invoices/{invoiceid}.html` @@ -31,13 +33,15 @@ Each query response contains a `Sozu-Id`. If you need help from our support team - Type: `GET` You can use optional parameters to filter it: -| Parameter | Type | Comment | -| :--- | :--- | :--- | -| `limit` | `int` | | -| `since` | `string` | ISO 8601 formatted | -| `until` | `string` | ISO 8601 formatted | + +| Parameter | Type | Comment | +| :-------- | :------- | :----------------- | +| `limit` | `int` | | +| `since` | `string` | ISO 8601 formatted | +| `until` | `string` | ISO 8601 formatted | Example response, for each invoice: + ```bash [ { @@ -87,11 +91,13 @@ Example response, for each invoice: ``` ## Deployments + - `/v4/orchestration/organisations/{ownerId}/applications/{applicationId}/deployments` - `/v4/orchestration/organisations/{ownerId}/applications/{applicationId}/deployments/{deploymentId}` - Type: `GET` Example response, for each deployment: + ```bash [ { @@ -134,22 +140,25 @@ Example response, for each deployment: ``` ## Instances + - `/v4/orchestration/organisations/{ownerId}/applications/{applicationId}/instances` - Type: `GET` You can use optional parameters to filter it: -| Parameter | Type | Comment | -| :--- | :--- | :--- | -| `deploymentId` | `string` | | -| `limit` | `int` | | -| `excludeDeleted` | `boolean`| | -| `since` | `string` | ISO 8601 formatted | -| `until` | `string` | ISO 8601 formatted | -| `order` | `string` | `ASC` or `DESC` | -| `includeState` | `string` | case sensitive, repeated | -| `excludeState` | `string` | case sensitive, repeated | + +| Parameter | Type | Comment | +| :--------------- | :-------- | :----------------------- | +| `deploymentId` | `string` | | +| `limit` | `int` | | +| `excludeDeleted` | `boolean` | | +| `since` | `string` | ISO 8601 formatted | +| `until` | `string` | ISO 8601 formatted | +| `order` | `string` | `ASC` or `DESC` | +| `includeState` | `string` | case sensitive, repeated | +| `excludeState` | `string` | case sensitive, repeated | Example response, for each instance: + ```bash [ { @@ -174,10 +183,12 @@ Example response, for each instance: ``` ## Load Balancers + - `/v4/load-balancers/organisations/{ownerId}/applications/{applicationId}/load-balancers/default` - Type: `GET` Example response: + ```bash [ { @@ -202,14 +213,14 @@ Example response: Answers with logs in a SSE (Server-Sent Events) stream. You can use optional parameters to filter it: | Parameter | Type | Comment | -| :--- | :--- | :--- | +| :-------------------------- | :------- | :----------------------------------------------------------------------------- | | `deploymentId` | `string` | | | `instanceId` | `string` | | | `since` | `string` | ISO 8601 formatted | | `until` | `string` | ISO 8601 formatted | | `field` | `string` | Allow to get only asked fields, repeated | | `filter` | `string` | Case insensitive `contains` type search | -| `limit` | `int64 ` | Minimum: `1` | +| `limit` | `int64` | Minimum: `1` | | `service` | `string` | systemd service name, repeated (default: `bas` and `bas-deploy`, can be `all`) | | `throttleElements` | `int` | Maximum elements read per `throttlePerInMilliseconds`, default `100` | | `throttlePerInMilliseconds` | `long` | Default `300` | @@ -232,27 +243,27 @@ Answers with logs in a SSE (Server-Sent Events) stream. You can use optional par The `recipient.type` discriminates the payload shape. Allowed values and their fields: -| `type` | Required (besides `type` and `url`) | Optional | -| :--- | :--- | :--- | -| `DATADOG` | — | — | -| `ELASTICSEARCH` | `index` | `username`, `password`, `tlsVerification` (`DEFAULT` or `TRUSTFUL`) | -| `NEWRELIC` | `apiKey` | — | -| `OVH_TCP` | — | `token`, `rfc5424StructuredDataParameters` | -| `RAW_HTTP` | — | `username`, `password` | -| `SYSLOG_TCP` | — | `rfc5424StructuredDataParameters` | -| `SYSLOG_UDP` | — | `rfc5424StructuredDataParameters` | +| `type` | Required (besides `type` and `url`) | Optional | +| :-------------- | :---------------------------------- | :------------------------------------------------------------------ | +| `DATADOG` | — | — | +| `ELASTICSEARCH` | `index` | `username`, `password`, `tlsVerification` (`DEFAULT` or `TRUSTFUL`) | +| `NEWRELIC` | `apiKey` | — | +| `OVH_TCP` | — | `token`, `rfc5424StructuredDataParameters` | +| `RAW_HTTP` | — | `username`, `password` | +| `SYSLOG_TCP` | — | `rfc5424StructuredDataParameters` | +| `SYSLOG_UDP` | — | `rfc5424StructuredDataParameters` | Field reference: -| Field | Type | Comment | -| :--- | :--- | :--- | -| `username` | `string` | Basic Auth username (`ELASTICSEARCH`, `RAW_HTTP`) | -| `password` | `string` | Basic Auth password (`ELASTICSEARCH`, `RAW_HTTP`) | -| `index` | `string` | Elasticsearch index name | -| `tlsVerification` | `string` | Elasticsearch TLS mode, `DEFAULT` (verify) or `TRUSTFUL` (skip verification) | -| `apiKey` | `string` | New Relic API key | -| `token` | `string` | OVHcloud Logs Data Platform token | -| `rfc5424StructuredDataParameters` | `string` | RFC 5424 structured data, e.g. `X-OVH-TOKEN="…"` | +| Field | Type | Comment | +| :-------------------------------- | :------- | :--------------------------------------------------------------------------- | +| `username` | `string` | Basic Auth username (`ELASTICSEARCH`, `RAW_HTTP`) | +| `password` | `string` | Basic Auth password (`ELASTICSEARCH`, `RAW_HTTP`) | +| `index` | `string` | Elasticsearch index name | +| `tlsVerification` | `string` | Elasticsearch TLS mode, `DEFAULT` (verify) or `TRUSTFUL` (skip verification) | +| `apiKey` | `string` | New Relic API key | +| `token` | `string` | OVHcloud Logs Data Platform token | +| `rfc5424StructuredDataParameters` | `string` | RFC 5424 structured data, e.g. `X-OVH-TOKEN="…"` | ### List logs drains @@ -280,12 +291,14 @@ Optional query parameters: `status`, `executionStatus`, `executionStatusNotIn`. - Type: `PATCH` — restarts log forwarding from the latest position. ## Operators + A Clever Cloud Operator deploys and manage resources for a given service. Available operators are: `keycloak`, `matomo`, `metabase`, `otoroshi`. - `/v4/addon-providers/addon-{operator_name}/addons/{operator_id}` - Type: `GET` Example response (Otoroshi — the richest payload): + ```json { "resourceId": "otoroshi_", @@ -328,7 +341,7 @@ All operators always return: `resourceId`, `addonId`, `name`, `ownerId`, `plan`, Other fields depend on the operator (`✓` = always returned, `—` = not returned, `?` = returned when set): | Field | `otoroshi` | `keycloak` | `matomo` | `metabase` | -| :--- | :---: | :---: | :---: | :---: | +| :---------------------- | :--------: | :--------: | :------: | :--------: | | `javaVersion` | ✓ | ✓ | — | ✓ | | `phpVersion` | — | — | ✓ | — | | `initialCredentials` | ✓ | ✓ | — | — | @@ -374,7 +387,9 @@ Available for `keycloak`, `metabase` and `otoroshi`. - Response: `application/yaml` — the full `otoroshictl`-compatible configuration of the instance. ### Network Groups + You can add the deployed service main application in a [Network Group](/doc/develop/network-groups/) to activate some enhanced features: + - `keycloak`: [Secured Multi Instances feature](/doc/addons/keycloak/#secured-multi-instances) - `otoroshi`: once in a Network Group, an Otoroshi instance can be used in front of Clever Cloud applications @@ -383,6 +398,7 @@ You can add the deployed service main application in a [Network Group](/doc/deve - Response code: `202` Response contains the full operator object with `networkGroup` feature enabled: + ```bash "features": { "networkGroup": { @@ -403,18 +419,20 @@ If no Network Group is found, the `networkGroup` feature value is `null`: ``` ## Products zones + - `/v4/products/zones` - `/v4/products/zones/{name}` - Type: `GET` You can use optional query parameters to filter the results: -| Parameter | Type | Comment | -| :--- | :--- | :--- | +| Parameter | Type | Comment | +| :-------- | :------- | :----------------------------------------------- | | `tag` | `string` | Filter zones on tags (e.g. `infra:clever-cloud`) | -| `ownerId` | `string` | Restrict to zones the given owner has access to | +| `ownerId` | `string` | Restrict to zones the given owner has access to | Example response, for each zone: + ```bash [ { @@ -434,12 +452,14 @@ Example response, for each zone: ``` ## Pulsar policies + - `/v4/addon-providers/addon-pulsar/addons/{addon-id}/storage-policies` - Type: `GET`/`PATCH` Define or get offload/retention policies of a [Pulsar add-on](/doc/addons/pulsar). Retention is how long messages are kept in the topic. Offload is how long messages are kept in the hot storage (NVMe SSD) before being moved to Cellar object storage (S3 compatible, HDD). Example query/response: + ```json { "retentionPolicies": { @@ -453,9 +473,9 @@ Example query/response: } ``` -| Field | Type | Comment | -| :--- | :--- | :--- | -| `sizeInBytes` | `int64` | Size threshold in bytes. Optional. A negative value means no limit. | +| Field | Type | Comment | +| :------------------ | :------ | :------------------------------------------------------------------------ | +| `sizeInBytes` | `int64` | Size threshold in bytes. Optional. A negative value means no limit. | | `durationInMinutes` | `int64` | Duration threshold in minutes. Optional. A negative value means no limit. | When both fields are omitted (or `null`), retention is infinite and offload is disabled. diff --git a/content/changelog/2023/11-10-healthcheck-for-java.md b/content/changelog/2023/11-10-healthcheck-for-java.md index 05eb39090..5b572a636 100644 --- a/content/changelog/2023/11-10-healthcheck-for-java.md +++ b/content/changelog/2023/11-10-healthcheck-for-java.md @@ -16,8 +16,8 @@ excludeSearch: true You can now define the paths that the orchestrator will call to validate a deployment for Java applications through one or multiple `CC_HEALTH_CHECK_PATH` environment variables. It works as follows: -* If the application responds on this specified path, and the response code is between `200` and `300`, the orchestrator considers that the deployment has been validated. -* If it responds with another error code outside this interval, the application is considered to have failed its deployment. +- If the application responds on this specified path, and the response code is between `200` and `300`, the orchestrator considers that the deployment has been validated. +- If it responds with another error code outside this interval, the application is considered to have failed its deployment. Currently, the orchestrator only checks whether the application is listening to TCP port `8080`. It does not always mean that the application has started correctly. diff --git a/content/changelog/2023/11-29-api-update.md b/content/changelog/2023/11-29-api-update.md index db764ad14..edf200d28 100644 --- a/content/changelog/2023/11-29-api-update.md +++ b/content/changelog/2023/11-29-api-update.md @@ -18,7 +18,7 @@ excludeSearch: true --- For years, you can deploy applications on Clever Cloud as a `Task` with the API or more recently the `CC_TASK=true` environment variable. As this feature will be widely available and used, the API has been updated to return such a state in the `instance.lifetime` object: -```json{filename="GET https://api.clever-cloud.com/v2/self/applications/",linenos=table,hl_lines=[17]} +```json {filename="GET https://api.clever-cloud.com/v2/self/applications/",linenos=table,hl_lines=[17]} { "id": "string", "name": "string", diff --git a/content/changelog/2023/12-13-cellar-migration-tool-update.md b/content/changelog/2023/12-13-cellar-migration-tool-update.md index 34880b757..aa178ba31 100644 --- a/content/changelog/2023/12-13-cellar-migration-tool-update.md +++ b/content/changelog/2023/12-13-cellar-migration-tool-update.md @@ -16,4 +16,4 @@ excludeSearch: true If you want to import your object storage data on Clever Cloud, you can use our open source [Cellar migration tool](https://github.com/CleverCloud/cellar-migration/releases/tag/v2.1.0), written in Rust. Its new release adds support for keep-alive when used from S3 clients and prevents panic if a network error occurs. - - Check this project [on GitHub](https://github.com/CleverCloud/cellar-migration) {{< icon "github" >}} +- Check this project [on GitHub](https://github.com/CleverCloud/cellar-migration) {{< icon "github" >}} diff --git a/content/changelog/2023/12-15-sozu-0.15.18.md b/content/changelog/2023/12-15-sozu-0.15.18.md index 3053f6ab9..01ceeff8f 100644 --- a/content/changelog/2023/12-15-sozu-0.15.18.md +++ b/content/changelog/2023/12-15-sozu-0.15.18.md @@ -15,7 +15,7 @@ excludeSearch: true Our open source reverse proxy, Sōzu, is now deployed in its version 0.15.18. It gives performance enhancements and: -* Fix 502 following a 304 response with a body that does not respect RFCs -* Fix of a panic when upgrading from HTTP to WS or from HTTPs to WSs -* Fix encryption issues when reusing TLS sessions (bump of Rustls) -* Added a `--json` flag to all commands +- Fix 502 following a 304 response with a body that does not respect RFCs +- Fix of a panic when upgrading from HTTP to WS or from HTTPs to WSs +- Fix encryption issues when reusing TLS sessions (bump of Rustls) +- Added a `--json` flag to all commands diff --git a/content/changelog/2024/01-10-documentation-changelog.md b/content/changelog/2024/01-10-documentation-changelog.md index 21d9d146a..6120806c2 100644 --- a/content/changelog/2024/01-10-documentation-changelog.md +++ b/content/changelog/2024/01-10-documentation-changelog.md @@ -20,4 +20,4 @@ aliases: excludeSearch: true --- -A changelog has been [added](/changelog) to our [Developer Center](). It will be regularly updated by the Clever Cloud teams to give you an overview of our new features, but also changes made and deprecations planned on the platform. +A changelog has been [added](/changelog) to our Developer Center. It will be regularly updated by the Clever Cloud teams to give you an overview of our new features, but also changes made and deprecations planned on the platform. diff --git a/content/changelog/2024/01-11-redis-7.2.4.md b/content/changelog/2024/01-11-redis-7.2.4.md index 1d30b2efd..da85ab9cb 100644 --- a/content/changelog/2024/01-11-redis-7.2.4.md +++ b/content/changelog/2024/01-11-redis-7.2.4.md @@ -19,12 +19,12 @@ Redis™ `7.2.4` is now available for each new deployment. You can easily migrat ## Security fixes -* (CVE-2023-41056) In some cases, Redis™ may incorrectly handle resizing of memory buffers which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution. +- (CVE-2023-41056) In some cases, Redis™ may incorrectly handle resizing of memory buffers which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution. ## Bug fixes -* Fix crashes of cluster commands clusters with mixed versions of 7.0 and 7.2 (#12805, #12832) -* Fix slot ownership not being properly handled when deleting a slot from a node (#12564) -* Fix atomicity issues with the RedisModuleEvent_Key module API event (#12733) +- Fix crashes of cluster commands clusters with mixed versions of 7.0 and 7.2 (#12805, #12832) +- Fix slot ownership not being properly handled when deleting a slot from a node (#12564) +- Fix atomicity issues with the RedisModuleEvent_Key module API event (#12733) - [Learn more about Redis™ 7.2.x branch](https://redis.com/blog/introducing-redis-7-2/). diff --git a/content/changelog/2024/01-22-matomo-5.0.1.md b/content/changelog/2024/01-22-matomo-5.0.1.md index f289ffe6a..a6fc72385 100644 --- a/content/changelog/2024/01-22-matomo-5.0.1.md +++ b/content/changelog/2024/01-22-matomo-5.0.1.md @@ -20,5 +20,6 @@ excludeSearch: true [Matomo](https://matomo.org/) 5.0 was released mid-December, version 5.0.1 is now available. Since then, we've worked on the support of this new branch on Clever Cloud. It's done! You can deploy this latest version right now from our [Console](https://console.clever-cloud.com) or [Clever Tools](https://github.com/CleverCloud/clever-tools). Existing customers add-ons are already up-to-date. + - [Learn more about Matomo 5.0](https://matomo.org/changelog/matomo-5-0-0/) - [Learn more about Matomo 5.0.1](https://matomo.org/changelog/matomo-5-0-1/) diff --git a/content/changelog/2024/01-24-ruby-3.3.0.md b/content/changelog/2024/01-24-ruby-3.3.0.md index fc248f7b8..9a0bc7c36 100644 --- a/content/changelog/2024/01-24-ruby-3.3.0.md +++ b/content/changelog/2024/01-24-ruby-3.3.0.md @@ -16,7 +16,7 @@ excludeSearch: true You can now choose the `3.3.0` release of Ruby when you deploy Ruby on Rails applications on Clever Cloud. It can be set either through your `Gemfile` or the `CC_RUBY_VERSION` [environment variable](/doc/reference/reference-environment-variables/#ruby). -* [Learn more about Ruby 3.3.0 release](https://www.ruby-lang.org/en/news/2023/12/25/ruby-3-3-0-released/) +- [Learn more about Ruby 3.3.0 release](https://www.ruby-lang.org/en/news/2023/12/25/ruby-3-3-0-released/) {{< callout type="info" >}} If you use Node.js on this image, be aware that default version is now 20.11.0 (LTS). If you need another release to deploy your application, set the `CC_NODE_VERSION` [environment variable](/doc/develop/env-variables/). diff --git a/content/changelog/2024/01-25-clever-tools-3.1.0.md b/content/changelog/2024/01-25-clever-tools-3.1.0.md index d2fa4e83c..c691b4826 100644 --- a/content/changelog/2024/01-25-clever-tools-3.1.0.md +++ b/content/changelog/2024/01-25-clever-tools-3.1.0.md @@ -18,6 +18,7 @@ excludeSearch: true --- For several months, we were preparing our CLI, [Clever Tools](https://github.com/CleverCloud/clever-tools/), for big changes. It starts to be ready for prime time with the 3.1.0 release. First, the packaging process moved to GitHub Actions with some improvements: + - You can now get the [list of releases](https://github.com/CleverCloud/clever-tools/releases) with a changelog and source code archives - No more beta channel: you'll be able to test new features easier - Better process, quicker tests, more releases! @@ -30,7 +31,7 @@ Of course, there are also some bug fixes. And be prepared: more new features are To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/02-07-clever-tools-3.2.0.md b/content/changelog/2024/02-07-clever-tools-3.2.0.md index 5d93fb106..306477632 100644 --- a/content/changelog/2024/02-07-clever-tools-3.2.0.md +++ b/content/changelog/2024/02-07-clever-tools-3.2.0.md @@ -19,15 +19,15 @@ excludeSearch: true As mentioned [in the previous release](../01-25-clever-tools-3.1.0/), the packaging process moved to GitHub Actions to provide a better process, quicker tests and more frequent releases. We start with [the 3.2.0](https://github.com/CleverCloud/clever-tools/releases/tag/3.2.0) adding the log auto retry feature on network failures and some fixes: -* **api:** improve error message with `EAI_AGAIN` and `ECONNRESET` ([b134213](https://github.com/CleverCloud/clever-tools/commit/b134213f30d46dd7f5690a38425deb4fd752148c)) -* **logs:** improve error message with `EAI_AGAIN` and `ECONNRESET` ([fada067](https://github.com/CleverCloud/clever-tools/commit/fada06771369173e579f5fd3a708ff3cef40c95f)) -* **logs:** improve open and error debug message ([28dd996](https://github.com/CleverCloud/clever-tools/commit/28dd9968bec8de9545c6b940be732d3f8f87a8f9)) -* **logs:** increase connection timeout ([a4ec4b9](https://github.com/CleverCloud/clever-tools/commit/a4ec4b90b5d3938e27679edeb7d375281def3776)) -* **logs:** only print SSE errors as debug when verbose mode is enabled ([3ea21c6](https://github.com/CleverCloud/clever-tools/commit/3ea21c6a4ff75db8df5f8177bba10ef17c2962e0)) +- **api:** improve error message with `EAI_AGAIN` and `ECONNRESET` ([b134213](https://github.com/CleverCloud/clever-tools/commit/b134213f30d46dd7f5690a38425deb4fd752148c)) +- **logs:** improve error message with `EAI_AGAIN` and `ECONNRESET` ([fada067](https://github.com/CleverCloud/clever-tools/commit/fada06771369173e579f5fd3a708ff3cef40c95f)) +- **logs:** improve open and error debug message ([28dd996](https://github.com/CleverCloud/clever-tools/commit/28dd9968bec8de9545c6b940be732d3f8f87a8f9)) +- **logs:** increase connection timeout ([a4ec4b9](https://github.com/CleverCloud/clever-tools/commit/a4ec4b90b5d3938e27679edeb7d375281def3776)) +- **logs:** only print SSE errors as debug when verbose mode is enabled ([3ea21c6](https://github.com/CleverCloud/clever-tools/commit/3ea21c6a4ff75db8df5f8177bba10ef17c2962e0)) To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/02-08-sozu-0.15.19.md b/content/changelog/2024/02-08-sozu-0.15.19.md index ba1500216..c7949c97b 100644 --- a/content/changelog/2024/02-08-sozu-0.15.19.md +++ b/content/changelog/2024/02-08-sozu-0.15.19.md @@ -20,4 +20,4 @@ Our open source reverse proxy, Sōzu, is now deployed in its version 0.15.19. We - Read the full changelog [on GitHub](https://github.com/sozu-proxy/sozu/releases/tag/0.15.19) {{< icon "github" >}} -We expect the next release to be 1.0, with [protobuf](https://protobuf.dev/) everywhere and 2x faster messaging. We'll now use [Metal I/O](https://github.com/tokio-rs/mio) also for communication between workers, rather than threadpools. A release candidate should be released this month. \ No newline at end of file +We expect the next release to be 1.0, with [protobuf](https://protobuf.dev/) everywhere and 2x faster messaging. We'll now use [Metal I/O](https://github.com/tokio-rs/mio) also for communication between workers, rather than threadpools. A release candidate should be released this month. diff --git a/content/changelog/2024/02-12-new-grahds-region.md b/content/changelog/2024/02-12-new-grahds-region.md index f5eed3f21..104a64fb9 100644 --- a/content/changelog/2024/02-12-new-grahds-region.md +++ b/content/changelog/2024/02-12-new-grahds-region.md @@ -19,4 +19,4 @@ For some time now, you can deploy your applications and add-ons through Clever C Some customers asked us to make the Gravelines HDS region available independently for applications and add-ons, we listened to them. You can now deploy to it from [`clever-tools`](https://github.com/CleverCloud/clever-tools) or [the Console](https://console.clever-cloud.com) as `grahds`. -Please note that Clever Cloud’s services and infrastructures obtained the ISO 9001 certification last year, we’re waiting for the definitive approval of ISO 27001. HDS certification is the next step and our current priority \ No newline at end of file +Please note that Clever Cloud’s services and infrastructures obtained the ISO 9001 certification last year, we’re waiting for the definitive approval of ISO 27001. HDS certification is the next step and our current priority diff --git a/content/changelog/2024/02-16-clever-tools-3.4.0.md b/content/changelog/2024/02-16-clever-tools-3.4.0.md index b1e899df4..be775b312 100644 --- a/content/changelog/2024/02-16-clever-tools-3.4.0.md +++ b/content/changelog/2024/02-16-clever-tools-3.4.0.md @@ -23,25 +23,25 @@ But it's the `create` and `deploy` commands which are the most improved by this To create a Node.js application once logged in, you can now just: -``` +```console clever create -t node ``` If you need a JSON response for a Go application deployed on Gravelines HDS: -``` +```console clever create --type go --region grahds --format json ``` Last but not least: you can deploy a specific tag from your local git repository adding the `--tag` or `-t` option: -``` +```console clever deploy --tag v0.4.2 ``` To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/02-22-postgresql-pgvector-support.md b/content/changelog/2024/02-22-postgresql-pgvector-support.md index d9828470a..d65d8d8da 100644 --- a/content/changelog/2024/02-22-postgresql-pgvector-support.md +++ b/content/changelog/2024/02-22-postgresql-pgvector-support.md @@ -26,4 +26,4 @@ We recently noticed an increased interest from our clients in supporting the `pg If you need `pgvector`, open a ticket to our support team through [the Console Ticket Center](https://console.clever-cloud.com/ticket-center-choice). If you need any other extension, feel free to ask, we will try our best to respond to your use-case. -- Learn more about `pgvector` [on GitHub](https://github.com/pgvector/pgvector) {{< icon "github" >}} \ No newline at end of file +- Learn more about `pgvector` [on GitHub](https://github.com/pgvector/pgvector) {{< icon "github" >}} diff --git a/content/changelog/2024/02-26-healthcheck-for-everyone.md b/content/changelog/2024/02-26-healthcheck-for-everyone.md index 2305a1465..74b2dbf69 100644 --- a/content/changelog/2024/02-26-healthcheck-for-everyone.md +++ b/content/changelog/2024/02-26-healthcheck-for-everyone.md @@ -17,8 +17,7 @@ Up until now, the deployment process only checked whether the application is lis You can now define the paths that the orchestrator will call to validate a deployment for applications through one or multiple `CC_HEALTH_CHECK_PATH` environment variables. It works as follows: -* If the application responds on this specified path, and the response code is between `200` and `300`, the orchestrator considers that the deployment has been validated. -* If it responds with a code outside this interval, the application is considered to have failed its deployment. +- If the application responds on this specified path, and the response code is between `200` and `300`, the orchestrator considers that the deployment has been validated. +- If it responds with a code outside this interval, the application is considered to have failed its deployment. - Read the documentation about [Deployment healthcheck path](/doc/develop/healthcheck/) 📖 - diff --git a/content/changelog/2024/02-26-mtl-iplb-update.md b/content/changelog/2024/02-26-mtl-iplb-update.md index c87876023..c9219b4fb 100644 --- a/content/changelog/2024/02-26-mtl-iplb-update.md +++ b/content/changelog/2024/02-26-mtl-iplb-update.md @@ -24,14 +24,14 @@ If you configure your domains through CNAME records, this update will be transpa ## Applications -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.mtl.clever-cloud.com.` | -| A
Only if CNAME is not available | @ 10800 IN A 158.69.109.229
@ 10800 IN A 149.56.117.183 | +| Record Type | Value | +| ----------------------------------- | ------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.mtl.clever-cloud.com.` | +| A
Only if CNAME is not available | @ 10800 IN A 158.69.109.229
@ 10800 IN A 149.56.117.183 | ## Add-ons -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain-sdc.mtl.clever-cloud.com.` | -| A
Only if CNAME is not available | @ 10800 IN A 54.39.154.128
@ 10800 IN A 167.114.35.164 | +| Record Type | Value | +| ----------------------------------- | ----------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain-sdc.mtl.clever-cloud.com.` | +| A
Only if CNAME is not available | @ 10800 IN A 54.39.154.128
@ 10800 IN A 167.114.35.164 | diff --git a/content/changelog/2024/02-27-images-update-part2.md b/content/changelog/2024/02-27-images-update-part2.md index 93365f43a..cd21f5470 100644 --- a/content/changelog/2024/02-27-images-update-part2.md +++ b/content/changelog/2024/02-27-images-update-part2.md @@ -18,11 +18,11 @@ Earlier this month, we published [a first set of updates](../02-02-images-update New images include security patches, Linux kernel 6.7.1, OpenSSL 3.2.1 and Node.js 20.11.0 by default. For the latter, you can set it via the `CC_NODE_VERSION` [environment variable](/doc/reference/reference-environment-variables/#commons-to-all-applications). Other changes are as follows: -* **.NET:** - * Version 8.0 support - * `CC_DOTNET_VERSION` [environment variable](/doc/reference/reference-environment-variables/#net) can be `6.0` or `8.0` -* **Java - Elastic / APM:** - * The agent has been updated to 1.47.1 - * Java 21 support +- **.NET:** + - Version 8.0 support + - `CC_DOTNET_VERSION` [environment variable](/doc/reference/reference-environment-variables/#net) can be `6.0` or `8.0` +- **Java - Elastic / APM:** + - The agent has been updated to 1.47.1 + - Java 21 support New PHP image is next to come, with 8.3 version support, we're currently working on it. After that, we'll bring our new strategy to build images into production. We're currently testing it, and preparing enhancements for multiples runtimes. Your applications will automatically benefit from it when available. [Stay tuned](/changelog/index.xml). diff --git a/content/changelog/2024/03-11-console-components-update.md b/content/changelog/2024/03-11-console-components-update.md index 396bb2e5a..47e28b193 100644 --- a/content/changelog/2024/03-11-console-components-update.md +++ b/content/changelog/2024/03-11-console-components-update.md @@ -17,4 +17,4 @@ aliases: excludeSearch: true --- -In our ongoing effort to improve our [Console](https://console.clever-cloud.com) experience, we upgraded some of its components. It now uses [JS Client 8.2.0](../02-27-js-client-8.2.0/) and [Web Components 13.0.0](../03-07-web-components-v13/). This update also brings some fixes regarding deployment logs links and Warp10 calls. \ No newline at end of file +In our ongoing effort to improve our [Console](https://console.clever-cloud.com) experience, we upgraded some of its components. It now uses [JS Client 8.2.0](../02-27-js-client-8.2.0/) and [Web Components 13.0.0](../03-07-web-components-v13/). This update also brings some fixes regarding deployment logs links and Warp10 calls. diff --git a/content/changelog/2024/04-04-CVE-2024-3094.md b/content/changelog/2024/04-04-CVE-2024-3094.md index 19b49cdc7..481c5c508 100644 --- a/content/changelog/2024/04-04-CVE-2024-3094.md +++ b/content/changelog/2024/04-04-CVE-2024-3094.md @@ -26,9 +26,9 @@ Nevertheless, we immediately put the backdoored version on a deny list to preven You can know more about this CVE following these links: -* https://boehs.org/node/everything-i-know-about-the-xz-backdoor -* https://gynvael.coldwind.pl/?lang=en&id=782 -* https://lcamtuf.substack.com/p/technologist-vs-spy-the-xz-backdoor -* https://www.openwall.com/lists/oss-security/2024/03/29/4 +- +- +- +- -You can contact [our support team](https://console.clever-cloud.com/ticket-center-choice) if you have further questions about this topic. \ No newline at end of file +You can contact [our support team](https://console.clever-cloud.com/ticket-center-choice) if you have further questions about this topic. diff --git a/content/changelog/2024/04-11-clever-tools-3.5.2.md b/content/changelog/2024/04-11-clever-tools-3.5.2.md index 6aec80c95..6c9bc406d 100644 --- a/content/changelog/2024/04-11-clever-tools-3.5.2.md +++ b/content/changelog/2024/04-11-clever-tools-3.5.2.md @@ -25,7 +25,7 @@ Clever Tasks can now be directly created and configured from Clever Tools. They' To create a Clever Task using Python for example: -``` +```console # We create a Python App and its Git repository mkdir pythonTask && cd pythonTask echo 'print("Hello, from a Clever Cloud Task!")' > task.py @@ -38,25 +38,27 @@ clever deploy If you want to check if an application will deploy as a `TASK` or the `REGULAR` way, use `clever status`. Change the executed command using an environment variable. Set another value, for example with a bash script: -``` +```console clever env set CC_RUN_COMMAND "bash a_bash_script.sh" ``` + Clever Tasks will evolve with enhancements planed over the coming months. Feel free to tell us about your needs or ideas on this! -* [Learn more about Clever Tasks](/doc/develop/tasks/) +- [Learn more about Clever Tasks](/doc/develop/tasks/) Last but not least, we're introducing the access to Materia KV. Those granted with alpha access can now create a serverless, synchronously-replicated (over our 3 data centers in Paris) key-value add-on. You'll be able to use it with multiples kind of clients. First to be supported is Redis API. Thus, you can run: -```` +````markdown clever addon create kv myKV source <(clever addon env addon_myKV_id -F shell) redis-cli -h $KV_HOST -p $KV_PORT --tls PING ```` -* [Learn more about Materia KV](https://www.clever.cloud/blog/company/2024/04/16/materiadb-kv-materia-functions/) ([FR](https://www.clever.cloud/fr/blog/entreprise/2024/04/16/materiadb-kv-functions/)) + +- [Learn more about Materia KV](https://www.clever.cloud/blog/company/2024/04/16/materiadb-kv-materia-functions/) ([FR](https://www.clever.cloud/fr/blog/entreprise/2024/04/16/materiadb-kv-functions/)) To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install) -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/04-15-new-logs-interface-public-beta.md b/content/changelog/2024/04-15-new-logs-interface-public-beta.md index 8e616c121..b0e555377 100644 --- a/content/changelog/2024/04-15-new-logs-interface-public-beta.md +++ b/content/changelog/2024/04-15-new-logs-interface-public-beta.md @@ -23,5 +23,5 @@ This new feature is available for **applications only**. In this new interface you'll get better access to your instance/deployments, dark/light themes, a text filter, multiple settings (stored into your Clever Cloud account). -* [Learn more about our new Logs interface](https://www.clever.cloud/blog/company/2024/04/15/our-new-logs-interface/) ([FR](https://www.clever.cloud/fr/blog/entreprise/2024/04/15/notre-nouvelle-interface-de-logs-est-disponible/)) -* [Give us feedback about new Logs interface](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface) +- [Learn more about our new Logs interface](https://www.clever.cloud/blog/company/2024/04/15/our-new-logs-interface/) ([FR](https://www.clever.cloud/fr/blog/entreprise/2024/04/15/notre-nouvelle-interface-de-logs-est-disponible/)) +- [Give us feedback about new Logs interface](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface) diff --git a/content/changelog/2024/04-16-clever-tools-3.6.0.md b/content/changelog/2024/04-16-clever-tools-3.6.0.md index 4a8baa7e0..86641a3c7 100644 --- a/content/changelog/2024/04-16-clever-tools-3.6.0.md +++ b/content/changelog/2024/04-16-clever-tools-3.6.0.md @@ -21,7 +21,7 @@ Clever Tools 3.6 is now available, with some minor fixes and a new feature: the To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install) -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/04-24-php-deprecate-warning.md b/content/changelog/2024/04-24-php-deprecate-warning.md index ecd29fec9..f82bf4fd5 100644 --- a/content/changelog/2024/04-24-php-deprecate-warning.md +++ b/content/changelog/2024/04-24-php-deprecate-warning.md @@ -21,4 +21,4 @@ As we still see a lot of applications using deprecated versions, the new PHP ima There is no short term plan to cut access to supported versions such as PHP 5.6 or 7.x, but you do it at your own risks. -* Learn more about [PHP releases lifecycle](https://www.php.net/supported-versions.php) +- Learn more about [PHP releases lifecycle](https://www.php.net/supported-versions.php) diff --git a/content/changelog/2024/04-25-images-update.md b/content/changelog/2024/04-25-images-update.md index 26e64b857..751ee2be0 100644 --- a/content/changelog/2024/04-25-images-update.md +++ b/content/changelog/2024/04-25-images-update.md @@ -16,17 +16,17 @@ excludeSearch: true We've updated all our images and deployed them without any impact for our users. They include our new action executor, which will help to make some features available soon. They also include the following changes: -* **Common:** - * Linux kernel 6.8.6 - * glibc security fix (CVE-2024-2961) - * Node.js 20.12.2 as default version -* **Ruby:** - * Update to 3.0.7, 3.1.5, 3.2.4 and 3.3.1 (CVE-2024-27282, CVE-2024-27281 et CVE-2024-27280) -* **Elixir:** - * Update to 21.16.2, 22.3.4.27, 23.3.4.20, 24.3.4.17, 25.3.2.11, 26.2.4 -* **.NET:** - * Update to 8.0.103, 8.0.3 +- **Common:** + - Linux kernel 6.8.6 + - glibc security fix (CVE-2024-2961) + - Node.js 20.12.2 as default version +- **Ruby:** + - Update to 3.0.7, 3.1.5, 3.2.4 and 3.3.1 (CVE-2024-27282, CVE-2024-27281 et CVE-2024-27280) +- **Elixir:** + - Update to 21.16.2, 22.3.4.27, 23.3.4.20, 24.3.4.17, 25.3.2.11, 26.2.4 +- **.NET:** + - Update to 8.0.103, 8.0.3 We had to delay the release of PHP image with 8.3 version support, to better support some extensions and include latest security fixes. It will start to deploy next week and include an informative message when a deprecated version of PHP is used. -* Learn more about the [message for deprecated PHP versions](../04-24-php-deprecate-warning/). +- Learn more about the [message for deprecated PHP versions](../04-24-php-deprecate-warning/). diff --git a/content/changelog/2024/05-30-php-8.3-java-22.md b/content/changelog/2024/05-30-php-8.3-java-22.md index 582470604..596ff31e5 100644 --- a/content/changelog/2024/05-30-php-8.3-java-22.md +++ b/content/changelog/2024/05-30-php-8.3-java-22.md @@ -16,17 +16,17 @@ excludeSearch: true We've updated some of our images and deployed them without any impact for our users. You can now set `CC_PHP_VERSION` to `8.3`. It's the default version for new PHP applications. If `CC_PHP_VERSION` is set to `8`, version 8.2 is still used. We also start to warn you if you use a [deprecated](https://www.php.net/supported-versions.php) PHP version, older than `8.1`. Other changes include: -* **Common:** - * Linux kernel 6.8.11 (PHP is still 6.8.6) - * Node.js 20.14.0 as default version -* **.NET, Go, Rust:** - * Fix for crons -* **.NET:** - * Fix for deployment issues with 8.x branch -* **Java:** - * Support for Java 22 (non LTS) - * Support for NewRelic with Java 22 -* **PHP:** - * Composer 2.7.6 - * Support for PHP 8.3 with [all supported extensions](/doc/applications/php/extensions/#available-extensions-and-modules) - * PHP info apps are available for PHP [8.0](https://php80info.cleverapps.io), [8.1](https://php81info.cleverapps.io), [8.2](https://php82info.cleverapps.io) and [8.3](https://php83info.cleverapps.io) +- **Common:** + - Linux kernel 6.8.11 (PHP is still 6.8.6) + - Node.js 20.14.0 as default version +- **.NET, Go, Rust:** + - Fix for crons +- **.NET:** + - Fix for deployment issues with 8.x branch +- **Java:** + - Support for Java 22 (non LTS) + - Support for NewRelic with Java 22 +- **PHP:** + - Composer 2.7.6 + - Support for PHP 8.3 with [all supported extensions](/doc/applications/php/extensions/#available-extensions-and-modules) + - PHP info apps are available for PHP [8.0](https://php80info.cleverapps.io), [8.1](https://php81info.cleverapps.io), [8.2](https://php82info.cleverapps.io) and [8.3](https://php83info.cleverapps.io) diff --git a/content/changelog/2024/06-07-sozu-1.0.2.md b/content/changelog/2024/06-07-sozu-1.0.2.md index 57cb09d30..b20a84eb5 100644 --- a/content/changelog/2024/06-07-sozu-1.0.2.md +++ b/content/changelog/2024/06-07-sozu-1.0.2.md @@ -23,5 +23,5 @@ The communication between processes is more efficient thanks to [ProtoBuf](https We've also fixed a bug affecting socket timeouts on frontends, ensuring better handling of requests. -* [New Sōzu website](https://www.sozu.io) -* Learn more about [Sōzu new releases](https://github.com/sozu-proxy/sozu/releases) {{< icon "github" >}} \ No newline at end of file +- [New Sōzu website](https://www.sozu.io) +- Learn more about [Sōzu new releases](https://github.com/sozu-proxy/sozu/releases) {{< icon "github" >}} diff --git a/content/changelog/2024/06-10-mongo-db-terraform.md b/content/changelog/2024/06-10-mongo-db-terraform.md index d0846cd88..68440e66c 100644 --- a/content/changelog/2024/06-10-mongo-db-terraform.md +++ b/content/changelog/2024/06-10-mongo-db-terraform.md @@ -21,4 +21,4 @@ If you can deploy on Clever Cloud through our [Console](https://console.clever-c We've recently added support for Python and [Materia KV](/doc/addons/materia-kv/). It's MongoDB's turn [to be available](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs/resources/mongodb). You can now create such an add-on, choose its plan, region and get the credentials to connect to it. -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2024/06-11-clever-tools-3.7.0.md b/content/changelog/2024/06-11-clever-tools-3.7.0.md index 528a5cb9f..4fb854d6e 100644 --- a/content/changelog/2024/06-11-clever-tools-3.7.0.md +++ b/content/changelog/2024/06-11-clever-tools-3.7.0.md @@ -21,7 +21,7 @@ excludeSearch: true To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/06-21-better-new-logs.md b/content/changelog/2024/06-21-better-new-logs.md index 4b098a1d2..67c703e2e 100644 --- a/content/changelog/2024/06-21-better-new-logs.md +++ b/content/changelog/2024/06-21-better-new-logs.md @@ -30,5 +30,4 @@ Some of you also asked for a change of the default behavior of this Logs interfa Depending on your feedback, we'll improve this further in the coming weeks. Here again, this is a first step as the indexed logs will open up a wide range of possibilities in this area. -* Share your comments and ideas on our [GitHub Community](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface) - +- Share your comments and ideas on our [GitHub Community](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface) diff --git a/content/changelog/2024/06-28-new-ip-list-paris.md b/content/changelog/2024/06-28-new-ip-list-paris.md index 76083b081..96a57c604 100644 --- a/content/changelog/2024/06-28-new-ip-list-paris.md +++ b/content/changelog/2024/06-28-new-ip-list-paris.md @@ -19,10 +19,10 @@ excludeSearch: true As our network evolves, we've updated [IP list for Paris (PAR) zone](/doc/administrate/domain-names/#your-application-runs-in-the-europeparis-par-zone). If you use A records for DNS configuration, update them. 4 new IP addresses should be added (`91.208.207.220` to `91.208.207.223`). **We strongly recommend using CNAME records instead.** -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.par.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 91.208.207.214`
`@ 10800 IN A 91.208.207.215`
`@ 10800 IN A 91.208.207.216`
`@ 10800 IN A 91.208.207.217`
`@ 10800 IN A 91.208.207.218`
`@ 10800 IN A 91.208.207.220`
`@ 10800 IN A 91.208.207.221`
`@ 10800 IN A 91.208.207.222`
`@ 10800 IN A 91.208.207.223` | +| Record Type | Value | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.par.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 91.208.207.214`
`@ 10800 IN A 91.208.207.215`
`@ 10800 IN A 91.208.207.216`
`@ 10800 IN A 91.208.207.217`
`@ 10800 IN A 91.208.207.218`
`@ 10800 IN A 91.208.207.220`
`@ 10800 IN A 91.208.207.221`
`@ 10800 IN A 91.208.207.222`
`@ 10800 IN A 91.208.207.223` | We are going to remove 4 IPs that **you must stop to use between now and August 23rd**: diff --git a/content/changelog/2024/07-01-matomo-5.1.0.md b/content/changelog/2024/07-01-matomo-5.1.0.md index 61884dd1a..65c9c6254 100644 --- a/content/changelog/2024/07-01-matomo-5.1.0.md +++ b/content/changelog/2024/07-01-matomo-5.1.0.md @@ -20,6 +20,7 @@ excludeSearch: true Our [Matomo](https://matomo.org/) add-on has been updated to version `5.1.0` which is now used by default. You can deploy this latest release from our [Console](https://console.clever-cloud.com) or [Clever Tools](https://github.com/CleverCloud/clever-tools). Existing customers add-ons are already up-to-date. Here are the changes since previous deployed version (`5.0.1`): + - [Learn more about Matomo 5.0.2](https://matomo.org/changelog/matomo-5-0-2/) - [Learn more about Matomo 5.0.3](https://matomo.org/changelog/matomo-5-0-3/) - [Learn more about Matomo 5.1.0](https://matomo.org/changelog/matomo-5-1-0/) diff --git a/content/changelog/2024/07-02-clever-tools-3.8.0.md b/content/changelog/2024/07-02-clever-tools-3.8.0.md index 7c6856a85..56354a1d2 100644 --- a/content/changelog/2024/07-02-clever-tools-3.8.0.md +++ b/content/changelog/2024/07-02-clever-tools-3.8.0.md @@ -28,6 +28,7 @@ clever restart --app app_id clever restart --app my_app_name clever restart --app my_app_name --org my_org_name ``` + `clever applications list` new command can be used from any folder of your system. It prints details about applications across your organisations. You can filter the result with `--org` or `-o` option. It includes IDs, names and local aliases (if available) in a table. ## More (consistent) options @@ -59,7 +60,7 @@ Multiple new features are also available in this "Big Summer Update". Access Log To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install) -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/07-03-docker-terraform.md b/content/changelog/2024/07-03-docker-terraform.md index ce7f86a62..8161b43c6 100644 --- a/content/changelog/2024/07-03-docker-terraform.md +++ b/content/changelog/2024/07-03-docker-terraform.md @@ -21,4 +21,4 @@ To manage your resources as code on Clever Cloud, from applications to add-ons ( You can define instances count or flavors, region, dependencies, ports, Dockerfile and application paths, registry credentials and more. Read [the full parameters list](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs/resources/docker). -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2024/07-04-apache-php-image-update.md b/content/changelog/2024/07-04-apache-php-image-update.md index fd3fec212..1b138a2a0 100644 --- a/content/changelog/2024/07-04-apache-php-image-update.md +++ b/content/changelog/2024/07-04-apache-php-image-update.md @@ -15,10 +15,11 @@ excludeSearch: true --- We've updated our PHP image to support versions [8.2.21](https://www.php.net/ChangeLog-8.php#8.2.21) and [8.3.9](https://www.php.net/ChangeLog-8.php#8.3.9): -* They fix a bug [introduced](https://github.com/api-platform/core/issues/6416) in 8.2.20 and 8.3.8 which we had previously patched -* This image includes a fix for GnuPG extension -* PHP info apps are up-to-date: [8.2](https://php82info.cleverapps.io) and [8.3](https://php83info.cleverapps.io) + +- They fix a bug [introduced](https://github.com/api-platform/core/issues/6416) in 8.2.20 and 8.3.8 which we had previously patched +- This image includes a fix for GnuPG extension +- PHP info apps are up-to-date: [8.2](https://php82info.cleverapps.io) and [8.3](https://php83info.cleverapps.io) Over the past few weeks, we have also included [Apache 2.4.60](https://dlcdn.apache.org/httpd/CHANGES_2.4.60), fixing CVE-2024-36387, 38472, 38473, 38474, 38475, 38476, 38477, 39573 and [Composer 2.7.7](https://github.com/composer/composer/releases/tag/2.7.7), fixing CVE-2024-35241, 35242. -* Learn more about [PHP on Clever Cloud](/doc/applications/php/) +- Learn more about [PHP on Clever Cloud](/doc/applications/php/) diff --git a/content/changelog/2024/07-05-go-haskell-ruby-update.md b/content/changelog/2024/07-05-go-haskell-ruby-update.md index 9a6813323..08bd88120 100644 --- a/content/changelog/2024/07-05-go-haskell-ruby-update.md +++ b/content/changelog/2024/07-05-go-haskell-ruby-update.md @@ -16,10 +16,10 @@ excludeSearch: true We've updated some of our images: -* **Go:** - * Default to 1.22.5 - * Fixes CVE-2024-2479 -* **Haskell:** - * Includes Stack 2.15.7 -* **Ruby:** - * Supports 3.1.6, 3.3.2 and 3.3.3 +- **Go:** + - Default to 1.22.5 + - Fixes CVE-2024-2479 +- **Haskell:** + - Includes Stack 2.15.7 +- **Ruby:** + - Supports 3.1.6, 3.3.2 and 3.3.3 diff --git a/content/changelog/2024/07-06-mysql-pgsql-update.md b/content/changelog/2024/07-06-mysql-pgsql-update.md index 245ea12c0..e9af4aefa 100644 --- a/content/changelog/2024/07-06-mysql-pgsql-update.md +++ b/content/changelog/2024/07-06-mysql-pgsql-update.md @@ -16,10 +16,10 @@ excludeSearch: true We've updated some of our database services: -* **MySQL:** - * Version 8.0.34-26 -* **PostgreSQL:** - * Version 15.7 - * `pgvector` extension 0.7.1 - * `timescaledb` extension 2.14.2 - * `pg_ivm` extension 1.8 is now available (ask it to [our support team](https://console.clever-cloud.com/ticket-center-choice)) +- **MySQL:** + - Version 8.0.34-26 +- **PostgreSQL:** + - Version 15.7 + - `pgvector` extension 0.7.1 + - `timescaledb` extension 2.14.2 + - `pg_ivm` extension 1.8 is now available (ask it to [our support team](https://console.clever-cloud.com/ticket-center-choice)) diff --git a/content/changelog/2024/07-10-CVE-2024-6387.md b/content/changelog/2024/07-10-CVE-2024-6387.md index 48dc6b7dd..c355d2792 100644 --- a/content/changelog/2024/07-10-CVE-2024-6387.md +++ b/content/changelog/2024/07-10-CVE-2024-6387.md @@ -22,4 +22,4 @@ As stated [by Qualys](https://www.qualys.com/regresshion-cve-2024-6387/), « *ve There was no known exploit for 64-bit systems, but we immediately strengthened firewall rules for port 22 and packaged OpenSSH 9.8p1. Then we deployed it on all our systems, images, managed services and started to redeploy applications gradually for our customers. This work is now completed on all our servers and thousands of virtual machines without any impact. -You can contact [our support team](https://console.clever-cloud.com/ticket-center-choice) if you have further questions about this topic. \ No newline at end of file +You can contact [our support team](https://console.clever-cloud.com/ticket-center-choice) if you have further questions about this topic. diff --git a/content/changelog/2024/07-17-keycloak-public-release.md b/content/changelog/2024/07-17-keycloak-public-release.md index 6d239a44c..be06367a8 100644 --- a/content/changelog/2024/07-17-keycloak-public-release.md +++ b/content/changelog/2024/07-17-keycloak-public-release.md @@ -19,4 +19,4 @@ excludeSearch: true Clever Cloud provides a Keycloak add-on, developed with Please Open-it, for some months in private alpha. We're releasing it publicly, with a new version and some fixes. Any Clever Cloud user can now deploy a Keycloak service from [API](/api/), [Clever Tools](https://github.com/CleverCloud/clever-tools) or [Console](https://console.clever-cloud.com) and use it. If you have any question or feedback, use our [GitHub Community space](https://github.com/CleverCloud/Community/discussions/categories/keycloak). -- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak/) \ No newline at end of file +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak/) diff --git a/content/changelog/2024/07-17-new-logs-default.md b/content/changelog/2024/07-17-new-logs-default.md index 1fdfcf5ab..512731d24 100644 --- a/content/changelog/2024/07-17-new-logs-default.md +++ b/content/changelog/2024/07-17-new-logs-default.md @@ -18,4 +18,4 @@ aliases: excludeSearch: true --- -New logs interface is available in the Console in public beta [for some months](../04-15-new-logs-interface-public-beta/), and we already [enhanced it](../06-21-better-new-logs/). We're now confident enough to use it by default. You can still disable it. If you have any question or feedback, use our [GitHub Community space](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface). \ No newline at end of file +New logs interface is available in the Console in public beta [for some months](../04-15-new-logs-interface-public-beta/), and we already [enhanced it](../06-21-better-new-logs/). We're now confident enough to use it by default. You can still disable it. If you have any question or feedback, use our [GitHub Community space](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface). diff --git a/content/changelog/2024/07-18-elastic-8.10.2-jenkins-2.452.3.md b/content/changelog/2024/07-18-elastic-8.10.2-jenkins-2.452.3.md index 0ef2f7240..3a39a4f5f 100644 --- a/content/changelog/2024/07-18-elastic-8.10.2-jenkins-2.452.3.md +++ b/content/changelog/2024/07-18-elastic-8.10.2-jenkins-2.452.3.md @@ -19,8 +19,8 @@ excludeSearch: true We've updated Elasticsearch and Jenkins services: -* **Elasticsearch:** - * Version 8.10.2 -* **Jenkins:** - * New add-ons are deployed with version 2.452.3 and Java 21 - * Existing Jenkins were upgraded to Java 17 +- **Elasticsearch:** + - Version 8.10.2 +- **Jenkins:** + - New add-ons are deployed with version 2.452.3 and Java 21 + - Existing Jenkins were upgraded to Java 17 diff --git a/content/changelog/2024/07-18-github-action-review-apps.md b/content/changelog/2024/07-18-github-action-review-apps.md index e2ab502e5..54b30b947 100644 --- a/content/changelog/2024/07-18-github-action-review-apps.md +++ b/content/changelog/2024/07-18-github-action-review-apps.md @@ -30,4 +30,4 @@ Some months ago, Clever Cloud released [a new GitHub Action](https://github.com/ type: '' ``` -- [Learn more about how to deploy from GitHub to Clever Cloud](/doc/ci-cd/github/) \ No newline at end of file +- [Learn more about how to deploy from GitHub to Clever Cloud](/doc/ci-cd/github/) diff --git a/content/changelog/2024/07-30-github-action-1.1.md b/content/changelog/2024/07-30-github-action-1.1.md index d06341a2b..6ab20ad8e 100644 --- a/content/changelog/2024/07-30-github-action-1.1.md +++ b/content/changelog/2024/07-30-github-action-1.1.md @@ -19,4 +19,4 @@ excludeSearch: true Update for [GitHub Action](https://github.com/marketplace/actions/clever-cloud-review-app-on-prs) to deploy applications from GitHub to Clever Cloud. Version 1.1 simplifies workflow files and gives conditional comments depending on the actions performed on the PR (deploy, update, delete). -- [Learn more about how to deploy from GitHub to Clever Cloud](/doc/ci-cd/github/) \ No newline at end of file +- [Learn more about how to deploy from GitHub to Clever Cloud](/doc/ci-cd/github/) diff --git a/content/changelog/2024/08-01-lagging-server-crate.md b/content/changelog/2024/08-01-lagging-server-crate.md index 9e60e2076..0fe8b56be 100644 --- a/content/changelog/2024/08-01-lagging-server-crate.md +++ b/content/changelog/2024/08-01-lagging-server-crate.md @@ -19,4 +19,4 @@ excludeSearch: true When we release new versions of our reverse proxy [Sōzu](https://www.sozu.io), we now systematically run performances tests. To conduct some of them directly from our CI, we developed a small tool: Lagging Server. It's open source, and now [available as a Rust crate](https://crates.io/crates/lagging_server) anyone can use. -* Learn more about [Lagging Server](https://github.com/CleverCloud/lagging_server) {{< icon "github" >}} \ No newline at end of file +- Learn more about [Lagging Server](https://github.com/CleverCloud/lagging_server) {{< icon "github" >}} diff --git a/content/changelog/2024/08-09-python-3.12-update.md b/content/changelog/2024/08-09-python-3.12-update.md index ec856d571..adc7eb7cf 100644 --- a/content/changelog/2024/08-09-python-3.12-update.md +++ b/content/changelog/2024/08-09-python-3.12-update.md @@ -19,10 +19,10 @@ excludeSearch: true We've updated Docker and Python image and deployed them without any impact for our users. -* **Common:** - * Linux kernel 6.10.2 -* **Docker:** - * Lighter updated image -* **Python:** - * Python 3.12 support - * A fix for missing system locales +- **Common:** + - Linux kernel 6.10.2 +- **Docker:** + - Lighter updated image +- **Python:** + - Python 3.12 support + - A fix for missing system locales diff --git a/content/changelog/2024/08-30-deploy-url-mtl.md b/content/changelog/2024/08-30-deploy-url-mtl.md index f86e8ba0b..9c81ffe73 100644 --- a/content/changelog/2024/08-30-deploy-url-mtl.md +++ b/content/changelog/2024/08-30-deploy-url-mtl.md @@ -19,7 +19,7 @@ excludeSearch: true We improved our git services on Montreal zone. Newly created applications now use `push-n2-mtl-clevercloud-customers.services.clever-cloud.com` as deployment URL. Applications still using the previous URL starting with `push-n1` won't be impacted by this change. If the new URL is not in your known hosts, you can see a warning during `git+ssh` operations: -``` +```text ❯ git clone git+ssh://git@push-n2-mtl-clevercloud-customers.services.clever-cloud.com/app_xxx.git Cloning into 'app_xxx'… The authenticity of host 'push-n2-mtl-clevercloud-customers.services.clever-cloud.com (192.99.188.152)' can't be established. diff --git a/content/changelog/2024/09-12-clever-tools-3.8.3.md b/content/changelog/2024/09-12-clever-tools-3.8.3.md index e10a75463..7f835b727 100644 --- a/content/changelog/2024/09-12-clever-tools-3.8.3.md +++ b/content/changelog/2024/09-12-clever-tools-3.8.3.md @@ -21,7 +21,7 @@ After two minor releases fixing issues with drains, [Clever Tools 3.8.3](https:/ To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/09-12-js-client-9.0.0.md b/content/changelog/2024/09-12-js-client-9.0.0.md index 49d2c39b2..ee4c8e186 100644 --- a/content/changelog/2024/09-12-js-client-9.0.0.md +++ b/content/changelog/2024/09-12-js-client-9.0.0.md @@ -17,4 +17,4 @@ aliases: excludeSearch: true --- -We released [Clever Cloud JS client 9.0.0](https://github.com/CleverCloud/clever-client.js/blob/master/CHANGELOG.md#900-2024-09-11). It computes OAuth v1 signature with Web Crypto for all platforms (Browser, Node.js, Deno, Bun, CF workers, etc.) instead of relying on `node:crypto`. This will simplify usage of the client with a bundler. \ No newline at end of file +We released [Clever Cloud JS client 9.0.0](https://github.com/CleverCloud/clever-client.js/blob/master/CHANGELOG.md#900-2024-09-11). It computes OAuth v1 signature with Web Crypto for all platforms (Browser, Node.js, Deno, Bun, CF workers, etc.) instead of relying on `node:crypto`. This will simplify usage of the client with a bundler. diff --git a/content/changelog/2024/10-01-python-image-changes.md b/content/changelog/2024/10-01-python-image-changes.md index 702944766..c361c2e74 100644 --- a/content/changelog/2024/10-01-python-image-changes.md +++ b/content/changelog/2024/10-01-python-image-changes.md @@ -22,7 +22,7 @@ We only support [pip](https://packaging.python.org/en/latest/tutorials/installin We'll enhance its native support in coming releases of our Python image. -* [Learn more about uv](https://github.com/astral-sh/uv) {{< icon "github" >}} +- [Learn more about uv](https://github.com/astral-sh/uv) {{< icon "github" >}} ## Python versions: time to clean up @@ -32,7 +32,7 @@ We'll also get closer to [the official Python release cycle](https://devguide.py If you need to sideload unsupported Python versions, `uv` [can help you](https://docs.astral.sh/uv/guides/install-python/). You can also deploy your applications through [Docker](/doc/applications/docker), but you should avoid to use end of life runtime in your applications. -* Learn more about [Python on Clever Cloud](/doc/applications/python/) +- Learn more about [Python on Clever Cloud](/doc/applications/python/) ## What's next? diff --git a/content/changelog/2024/10-07-CVE-2024-47561.md b/content/changelog/2024/10-07-CVE-2024-47561.md index 0629706de..7b3123c65 100644 --- a/content/changelog/2024/10-07-CVE-2024-47561.md +++ b/content/changelog/2024/10-07-CVE-2024-47561.md @@ -15,4 +15,4 @@ excludeSearch: true On October 4th, we were informed of [a schema parsing vulnerability](https://nvd.nist.gov/vuln/detail/CVE-2024-47561) in the Java SDK of Apache Avro 1.11.3 and previous versions, allowing bad actors to execute arbitrary code. Our security team instantly made sanity checks, and listed impacted services. -We upgraded our tools using Avro to 1.11.4. As Apache Pulsar was impacted, we upgraded our cluster to [version 3.3.2](https://github.com/apache/pulsar/releases/tag/v3.3.2). The complete rolling of these updates is now ended, without any impact to our users. You can contact [our support team](https://console.clever-cloud.com/ticket-center-choice) if you have further questions about this topic. \ No newline at end of file +We upgraded our tools using Avro to 1.11.4. As Apache Pulsar was impacted, we upgraded our cluster to [version 3.3.2](https://github.com/apache/pulsar/releases/tag/v3.3.2). The complete rolling of these updates is now ended, without any impact to our users. You can contact [our support team](https://console.clever-cloud.com/ticket-center-choice) if you have further questions about this topic. diff --git a/content/changelog/2024/10-17-sozu-1.0.5.md b/content/changelog/2024/10-17-sozu-1.0.5.md index 1d716de94..9c4431afc 100644 --- a/content/changelog/2024/10-17-sozu-1.0.5.md +++ b/content/changelog/2024/10-17-sozu-1.0.5.md @@ -20,4 +20,4 @@ excludeSearch: true We're currently testing the 1.1.0 release candidate of Sōzu, with HTTP/2 support, coming soon on Clever Cloud. -* Learn more about [Sōzu new releases](https://github.com/sozu-proxy/sozu/releases) {{< icon "github" >}} \ No newline at end of file +- Learn more about [Sōzu new releases](https://github.com/sozu-proxy/sozu/releases) {{< icon "github" >}} diff --git a/content/changelog/2024/10-21-clever-tools-3.9.0.md b/content/changelog/2024/10-21-clever-tools-3.9.0.md index 9b71dfac0..2b9859927 100644 --- a/content/changelog/2024/10-21-clever-tools-3.9.0.md +++ b/content/changelog/2024/10-21-clever-tools-3.9.0.md @@ -20,6 +20,7 @@ excludeSearch: true [Clever Tools 3.9](https://github.com/CleverCloud/clever-tools/releases/tag/3.9.0) is available. It's the first minor release since our [Big Summer Update](../07-02-clever-tools-3.8.0/). Over the past few months, we've revamped many things under the hood, to clean the code and prepare next big changes we're working on. ## List and diagnose your domains + This new version brings two new features to manage domains. First is `clever domain overview`, allowing you to list domains linked to your account, across all the organisations you manage. List only domains containing a specific text string in their name thanks to the `--filter` option. Use the `json` output (`--format`/`-F`) and tools such as [jless](https://jless.io/) to navigate through long lists easily. This feature uses a new "short" link for the Console, which doesn't require organisation or user ID: @@ -37,7 +38,7 @@ This release also provides bug fixes, the token expiry date/time in `clever prof To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/11-08-erlang-rust-update.md b/content/changelog/2024/11-08-erlang-rust-update.md index 94f2319e4..a302aaca8 100644 --- a/content/changelog/2024/11-08-erlang-rust-update.md +++ b/content/changelog/2024/11-08-erlang-rust-update.md @@ -17,16 +17,16 @@ aliases: We’ve updated Elixir and Rust images. They were deployed without any impact for our users. -* **Common:** - * Linux kernel 6.11.6 - * Lighter Vector binary - * New build/deploy message with colors - * Users can now use multiple OpenVPN clients in a single instance -* **Elixir:** - * Elixir 1.17 - * Erlang 27.1.2 -* **Rust:** - * Rust 1.82.0 - * libassuan fix +- **Common:** + - Linux kernel 6.11.6 + - Lighter Vector binary + - New build/deploy message with colors + - Users can now use multiple OpenVPN clients in a single instance +- **Elixir:** + - Elixir 1.17 + - Erlang 27.1.2 +- **Rust:** + - Rust 1.82.0 + - libassuan fix We also started to add [Redirection.io](https://redirection.io) agent in our images with this update, as part of our [Enthusiast Tools initiative](../10-01-python-image-changes/), and plan to ease its native support in coming releases. diff --git a/content/changelog/2024/11-08-mysql-mysql-8.4.0.md b/content/changelog/2024/11-08-mysql-mysql-8.4.0.md index 22003ae2c..98b9e3d36 100644 --- a/content/changelog/2024/11-08-mysql-mysql-8.4.0.md +++ b/content/changelog/2024/11-08-mysql-mysql-8.4.0.md @@ -16,5 +16,5 @@ excludeSearch: true MySQL 8.4 is available on Clever Cloud. You can deploy it as a new add-on or migrate existing ones. As this is the first release (8.4.0) of this long term support (LTS) branch for the [Percona Server](https://www.percona.com/mysql/software/percona-server-for-mysql) we use, it's tagged as `early`. That means you should consider it mostly to make some tests and discover what's new. But we recommend [waiting a bit](https://www.percona.com/blog/do-not-upgrade-to-any-version-of-mysql-after-8-0-37/) before using a new branch in production. -* [Learn more about MySQL 8.4](https://www.percona.com/blog/mysql-8-4-first-peek/) -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.4](https://www.percona.com/blog/mysql-8-4-first-peek/) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) diff --git a/content/changelog/2024/11-09-java-update.md b/content/changelog/2024/11-09-java-update.md index 3a34cff24..255f943fb 100644 --- a/content/changelog/2024/11-09-java-update.md +++ b/content/changelog/2024/11-09-java-update.md @@ -16,11 +16,11 @@ excludeSearch: true We’ve updated Java image. It was deployed without any impact for our users. - * Java 23 support - * Linux kernel 6.11.6 - * Lighter Vector binary - * New build/deploy message with colors - * [Redirection.io](https://redirection.io) agent is included in the image - * Users can now use multiple OpenVPN clients in a single instance +- Java 23 support +- Linux kernel 6.11.6 +- Lighter Vector binary +- New build/deploy message with colors +- [Redirection.io](https://redirection.io) agent is included in the image +- Users can now use multiple OpenVPN clients in a single instance -As Java 22 is now [considered as end-of-life](https://www.oracle.com/fr/java/technologies/java-se-support-roadmap.html), we invite you to upgrade `CC_JAVA_VERSION` of your applications to `23` or to `21` (latest LTS). \ No newline at end of file +As Java 22 is now [considered as end-of-life](https://www.oracle.com/fr/java/technologies/java-se-support-roadmap.html), we invite you to upgrade `CC_JAVA_VERSION` of your applications to `23` or to `21` (latest LTS). diff --git a/content/changelog/2024/11-21-eol-warnings-console.md b/content/changelog/2024/11-21-eol-warnings-console.md index ef24b35ad..8b1d7d6df 100644 --- a/content/changelog/2024/11-21-eol-warnings-console.md +++ b/content/changelog/2024/11-21-eol-warnings-console.md @@ -18,6 +18,7 @@ excludeSearch: true --- [Earlier this year](../04-24-php-deprecate-warning/), we started to show warnings in logs when you deploy PHP applications with a `CC_PHP_VERSION` corresponding to a release considered as [end-of-life by PHP community](https://www.php.net/supported-versions.php). Today, we start showing such warnings in the environment variables panel of applications in the Clever Cloud Console for: + - `CC_PHP_VERSION` - `CC_PYTHON_VERSION` - `CC_RUBY_VERSION` diff --git a/content/changelog/2024/11-27-clever-tools-3.10.md b/content/changelog/2024/11-27-clever-tools-3.10.md index 2e4584c9e..f29e756ae 100644 --- a/content/changelog/2024/11-27-clever-tools-3.10.md +++ b/content/changelog/2024/11-27-clever-tools-3.10.md @@ -25,7 +25,7 @@ clever addon create es-addon --option plugins=analysis-icu,mapper-murmur3 To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/12-12-python-3.13-update.md b/content/changelog/2024/12-12-python-3.13-update.md index 50cd6c46d..d158dc909 100644 --- a/content/changelog/2024/12-12-python-3.13-update.md +++ b/content/changelog/2024/12-12-python-3.13-update.md @@ -16,10 +16,10 @@ excludeSearch: true We deployed an updated Python image with no impact for our users. - * uv 0.5.7 - * Linux kernel 6.11.6 - * Redirection.io agent - * Python 3.13 support - * Python 3.7 withdrawal +- uv 0.5.7 +- Linux kernel 6.11.6 +- Redirection.io agent +- Python 3.13 support +- Python 3.7 withdrawal -You can now use `3.13` as `CC_PYTHON_VERSION` environment variable. This version will also be used as default value. As announced [in October](../10-01-python-image-changes), Python 3.7 is not supported anymore. However, you can use `uv` to [install or use Python 3.7](https://docs.astral.sh/uv/guides/install-python/#installing-a-specific-version). \ No newline at end of file +You can now use `3.13` as `CC_PYTHON_VERSION` environment variable. This version will also be used as default value. As announced [in October](../10-01-python-image-changes), Python 3.7 is not supported anymore. However, you can use `uv` to [install or use Python 3.7](https://docs.astral.sh/uv/guides/install-python/#installing-a-specific-version). diff --git a/content/changelog/2024/12-18-clever-tools-3.11.md b/content/changelog/2024/12-18-clever-tools-3.11.md index c62f53b6c..7863d9b39 100644 --- a/content/changelog/2024/12-18-clever-tools-3.11.md +++ b/content/changelog/2024/12-18-clever-tools-3.11.md @@ -30,7 +30,7 @@ You can learn more about `clever kv` in [Clever Tools documentation](/doc/cli/kv To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2024/12-24-terraform-0.5-Keycloak-Metabase.md b/content/changelog/2024/12-24-terraform-0.5-Keycloak-Metabase.md index 475c109c8..071370189 100644 --- a/content/changelog/2024/12-24-terraform-0.5-Keycloak-Metabase.md +++ b/content/changelog/2024/12-24-terraform-0.5-Keycloak-Metabase.md @@ -20,4 +20,4 @@ excludeSearch: true You can now deploy [Keycloak](/doc/addons/keycloak) and [Metabase](/doc/addons/metabase) add-ons using our Terraform provider, compatible with OpenTofu. -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/01-06-sozu-1.0.6.md b/content/changelog/2025/01-06-sozu-1.0.6.md index c619f958c..7b2583552 100644 --- a/content/changelog/2025/01-06-sozu-1.0.6.md +++ b/content/changelog/2025/01-06-sozu-1.0.6.md @@ -18,4 +18,4 @@ excludeSearch: true [Sōzu](https://www.sozu.io) 1.0.6 is now available and deployed on all our shared and dedicated load balancers. It comes with a fix for a TLS bug we encountered with [Metabase](/doc/addons/metabase) instances in some cases. We've also overhauled the custom pages for 4xx and 5xx errors. They now display a better designed and more detailed message. -* Learn more about [Sōzu new releases](https://github.com/sozu-proxy/sozu/releases) {{< icon "github" >}} +- Learn more about [Sōzu new releases](https://github.com/sozu-proxy/sozu/releases) {{< icon "github" >}} diff --git a/content/changelog/2025/01-15-node-22-lts-image-update.md b/content/changelog/2025/01-15-node-22-lts-image-update.md index 4b3122e77..8c4d23739 100644 --- a/content/changelog/2025/01-15-node-22-lts-image-update.md +++ b/content/changelog/2025/01-15-node-22-lts-image-update.md @@ -18,7 +18,7 @@ We deployed an updated Node.js image with no impact for our users. It uses `22.1 It's the first image to include the [Mise package manager](https://mise.jdx.dev/), allowing you to install and manage many developer tools, environment variables, secrets, aliases, tasks, hooks through commands or a configuration file. It's compatible with [asdf ecosystem](https://mise.jdx.dev/dev-tools/comparison-to-asdf.html). -* Learn more about [Mise](https://mise.jdx.dev/) +- Learn more about [Mise](https://mise.jdx.dev/) ## Redirection.io easy setup diff --git a/content/changelog/2025/02-05-mysql-update.md b/content/changelog/2025/02-05-mysql-update.md index bad195d46..23ed49d37 100644 --- a/content/changelog/2025/02-05-mysql-update.md +++ b/content/changelog/2025/02-05-mysql-update.md @@ -14,5 +14,5 @@ excludeSearch: true We updated MySQL images for releases 8.4 (8.4.2-2) and 8.0 (8.0.39-30). A new option is available at database creation from Clever Tools: [skip-log-bin](https://dev.mysql.com/doc/refman/8.4/en/replication-options-binary-log.html#option_mysqld_log-bin). To use it, add `--option skip-log-bin` flag with the `clever create addon mysql-addon` command. -* [Learn more about MySQL 8.4](https://www.percona.com/blog/mysql-8-4-first-peek/) -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.4](https://www.percona.com/blog/mysql-8-4-first-peek/) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) diff --git a/content/changelog/2025/02-10-github-action-2.0.md b/content/changelog/2025/02-10-github-action-2.0.md index ee557b4f8..9717c2690 100644 --- a/content/changelog/2025/02-10-github-action-2.0.md +++ b/content/changelog/2025/02-10-github-action-2.0.md @@ -17,4 +17,4 @@ excludeSearch: true Update for [GitHub Action](https://github.com/marketplace/actions/clever-cloud-review-app-on-prs) to deploy applications from GitHub to Clever Cloud. Version 2.0 brings an improved interface for the comment section on deployments, as well as updated security practices in names and docs. -- [Learn more about how to deploy from GitHub to Clever Cloud](/doc/ci-cd/github/) \ No newline at end of file +- [Learn more about how to deploy from GitHub to Clever Cloud](/doc/ci-cd/github/) diff --git a/content/changelog/2025/02-12-php-8.4-update.md b/content/changelog/2025/02-12-php-8.4-update.md index 8d37e147a..2fd2c3b0e 100644 --- a/content/changelog/2025/02-12-php-8.4-update.md +++ b/content/changelog/2025/02-12-php-8.4-update.md @@ -14,14 +14,14 @@ excludeSearch: true We deployed an updated PHP image with no impact for our users. - * PHP 8.4.3 - * Composer 2.8.5 - * Symfony CLI 5.10.7 - * Linux kernel 6.12.9 +- PHP 8.4.3 +- Composer 2.8.5 +- Symfony CLI 5.10.7 +- Linux kernel 6.12.9 You can now use `8.4` as `CC_PHP_VERSION` environment variable, `8.3` is still the default for now. Only some extensions support PHP 8.4 for now: apcu, amqp, blackfire, event, elastic-apm, gnupg, grpc, imap, mailparse, maxminddb, memcache, memcached, mongodb, newrelic, oauth, pdflib, protobuf pspell, rdkafka, redis, ssh2, solr, tideways, uploadprogress, zip. We'll add support for more extensions as they are released. -* [PHP migration guide from 8.3 to 8.4](https://www.php.net/migration84) -* [Learn more about PHP on Clever Cloud](/doc/applications/php/) +- [PHP migration guide from 8.3 to 8.4](https://www.php.net/migration84) +- [Learn more about PHP on Clever Cloud](/doc/applications/php/) diff --git a/content/changelog/2025/02-19-terraform-0.6-redis.md b/content/changelog/2025/02-19-terraform-0.6-redis.md index c625f53c0..dcbc0b168 100644 --- a/content/changelog/2025/02-19-terraform-0.6-redis.md +++ b/content/changelog/2025/02-19-terraform-0.6-redis.md @@ -17,4 +17,4 @@ excludeSearch: true You can now deploy [Redis®](/doc/addons/redis) add-ons using our Terraform provider, compatible with OpenTofu. The [0.6.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v0.6.0) of the Clever Cloud Terraform provider also introduce the resource update. You can now apply a new plan without destroying and recreating the resource starting with PHP applications. -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/02-20-logs-interface-old-decomission.md b/content/changelog/2025/02-20-logs-interface-old-decomission.md index 094bbb2f6..3b2ed6c34 100644 --- a/content/changelog/2025/02-20-logs-interface-old-decomission.md +++ b/content/changelog/2025/02-20-logs-interface-old-decomission.md @@ -19,4 +19,4 @@ excludeSearch: true Next, we'll use the new stack for add-ons, access logs and continue improving the interface. A big step will be to introduce logs indexing, allowing you to better search through your logs and load the latest. -Feel free to ask for features and improvements in our [GitHub Community space](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface). \ No newline at end of file +Feel free to ask for features and improvements in our [GitHub Community space](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface). diff --git a/content/changelog/2025/03-03-images-update.md b/content/changelog/2025/03-03-images-update.md index ae5203039..580082275 100644 --- a/content/changelog/2025/03-03-images-update.md +++ b/content/changelog/2025/03-03-images-update.md @@ -14,24 +14,24 @@ excludeSearch: true We deployed and updated all our images, except Docker, Go, Java, Node and Ruby, with no impact for our users. -* **Common:** - * Linux kernel 6.13.5 -* **.NET:** - * Update to 8.0.108 -* **Elixir:** - * Update to 1.18.2 - * Erlang 27.2.2 -* **Haskell:** - * Stack 2.15.7 -* **PHP:** - * Update to 8.4.4 - * Composer 2.8.6 - * `CC_APACHE_HEADERS_SIZE` [environment variable](/doc/reference/reference-environment-variables/) -* **Python:** - * Update to 3.13.2 - * pip 24.3.1 - * uv 0.6.3 -* **Rust:** - * Update to 1.85.0 +- **Common:** + - Linux kernel 6.13.5 +- **.NET:** + - Update to 8.0.108 +- **Elixir:** + - Update to 1.18.2 + - Erlang 27.2.2 +- **Haskell:** + - Stack 2.15.7 +- **PHP:** + - Update to 8.4.4 + - Composer 2.8.6 + - `CC_APACHE_HEADERS_SIZE` [environment variable](/doc/reference/reference-environment-variables/) +- **Python:** + - Update to 3.13.2 + - pip 24.3.1 + - uv 0.6.3 +- **Rust:** + - Update to 1.85.0 All these images include [Mise and Redirection.io easy setup](/changelog/2025/01-15-node-22-lts-image-update/). diff --git a/content/changelog/2025/03-07-clever-tools-3.12.md b/content/changelog/2025/03-07-clever-tools-3.12.md index 0f7fca1e9..6e5bb2ace 100644 --- a/content/changelog/2025/03-07-clever-tools-3.12.md +++ b/content/changelog/2025/03-07-clever-tools-3.12.md @@ -18,6 +18,7 @@ excludeSearch: true [Clever Tools 3.12](https://github.com/CleverCloud/clever-tools/releases/tag/3.12.0) is available. It includes some bug fixes and two new experimental features, available through feature flags. ## API Tokens + First is `clever tokens`, which allows you to create and manage API tokens, and use them to request the Clever Cloud API: ```bash @@ -26,6 +27,7 @@ clever features enable tokens clever tokens create "CI job Foobar" clever tokens create "Quick local test" --expiration 1h ``` + You can also list or revoke tokens: ```bash @@ -38,6 +40,7 @@ Once created, API tokens must be used through the bridge URL: ```bash curl https://api-bridge.clever-cloud.com/v2/self -H "Authorization: Bearer [API_TOKEN]" ``` + - [Clever Cloud API Documentation](/api) - [Give your feedback about API Tokens](https://github.com/CleverCloud/Community/discussions/categories/api-tokens) @@ -97,9 +100,10 @@ As this beta feature evolves, we'll add demos, documentations and videos to help - [Give your feedback about Network Groups](https://github.com/CleverCloud/Community/discussions/categories/network-groups) ## How to upgrade + To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/03-12-images-update.md b/content/changelog/2025/03-12-images-update.md index de3169d5f..dd19c54fe 100644 --- a/content/changelog/2025/03-12-images-update.md +++ b/content/changelog/2025/03-12-images-update.md @@ -14,17 +14,17 @@ excludeSearch: true We deployed and updated all our images, except Java and Ruby, with no impact for our users. -* **Common:** - * Linux kernel 6.13.6 -* **Docker:** - * Update to 28.0.1 -* **Elixir:** - * Update to 1.18.3 - * Erlang 27.3 -* **Go:** - * Update to 1.24.1 -* **Node.js:** - * Update to 22.14.0 -* **Python:** - * pip 25.0.1 - * uv 0.6.4 +- **Common:** + - Linux kernel 6.13.6 +- **Docker:** + - Update to 28.0.1 +- **Elixir:** + - Update to 1.18.3 + - Erlang 27.3 +- **Go:** + - Update to 1.24.1 +- **Node.js:** + - Update to 22.14.0 +- **Python:** + - pip 25.0.1 + - uv 0.6.4 diff --git a/content/changelog/2025/03-12-materia-kv-json.md b/content/changelog/2025/03-12-materia-kv-json.md index 1c88e1dff..904ff81fd 100644 --- a/content/changelog/2025/03-12-materia-kv-json.md +++ b/content/changelog/2025/03-12-materia-kv-json.md @@ -16,6 +16,7 @@ excludeSearch: true --- Materia KV now supports some `JSON` commands : + - `JSON.SET`: sets JSON value at path in key - `JSON.GET`: gets JSON value at path from key - `JSON.DEL`: deletes JSON value at path from key diff --git a/content/changelog/2025/03-18-images-update-ruby-unified.md b/content/changelog/2025/03-18-images-update-ruby-unified.md index a562da904..1d21625a4 100644 --- a/content/changelog/2025/03-18-images-update-ruby-unified.md +++ b/content/changelog/2025/03-18-images-update-ruby-unified.md @@ -14,17 +14,17 @@ excludeSearch: true We deployed and updated all our images, except Java, with no impact for our users. It now includes Ruby as part as our new unified image stack. -* **Common:** - * Linux kernel 6.13.7 -* **PHP:** - * Update to 8.4.5 - * Apache 2.4.63 -* **Python:** - * uv 0.6.6 - * nginx 1.26.3 -* **Ruby:** - * Update to 3.4.2 - * Bundler 2.6.5 - * nginx 1.26.3 +- **Common:** + - Linux kernel 6.13.7 +- **PHP:** + - Update to 8.4.5 + - Apache 2.4.63 +- **Python:** + - uv 0.6.6 + - nginx 1.26.3 +- **Ruby:** + - Update to 3.4.2 + - Bundler 2.6.5 + - nginx 1.26.3 Note that Ruby 3.4 is the default only in Ruby applications. Ruby 3.2 is still default on other runtimes, use [rbenv](https://github.com/rbenv/rbenv) and `RBENV_VERSION` to switch to Ruby 3.4 for now. diff --git a/content/changelog/2025/03-18-mysql-update.md b/content/changelog/2025/03-18-mysql-update.md index 4de4fe4f8..3693d7a89 100644 --- a/content/changelog/2025/03-18-mysql-update.md +++ b/content/changelog/2025/03-18-mysql-update.md @@ -14,6 +14,6 @@ excludeSearch: true We updated MySQL images 8.0 and 8.4 from Percona Server. They're now available for add-ons creation and migration. -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) -* [Learn more about MySQL 8.0.41-32](https://docs.percona.com/percona-server/8.0/release-notes/8.0.41-32.html) -* [Learn more about MySQL 8.4.3-3](https://docs.percona.com/percona-server/8.4/release-notes/8.4.3-3.html) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.0.41-32](https://docs.percona.com/percona-server/8.0/release-notes/8.0.41-32.html) +- [Learn more about MySQL 8.4.3-3](https://docs.percona.com/percona-server/8.4/release-notes/8.4.3-3.html) diff --git a/content/changelog/2025/03-18-postgresql-16-17.md b/content/changelog/2025/03-18-postgresql-16-17.md index c76eb2bae..c8d200435 100644 --- a/content/changelog/2025/03-18-postgresql-16-17.md +++ b/content/changelog/2025/03-18-postgresql-16-17.md @@ -16,5 +16,5 @@ PostgreSQL 16.8 and 17.4 are now available for add-on creation and migration. So We also updated PostgreSQL 15 to release 15.12, PostgreSQL 14 to release 14.17, PostgreSQL 13 to release 13.20. Migrate your database to the desired major version to get the latest release. -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) -* [PostgreSQL release notes](https://www.postgresql.org/docs/release/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [PostgreSQL release notes](https://www.postgresql.org/docs/release/) diff --git a/content/changelog/2025/03-19-paris-hds.md b/content/changelog/2025/03-19-paris-hds.md index 5de69976b..31355c410 100644 --- a/content/changelog/2025/03-19-paris-hds.md +++ b/content/changelog/2025/03-19-paris-hds.md @@ -14,5 +14,5 @@ excludeSearch: true Clever Cloud is HDS-certified for [all six activities](https://www.clever.cloud/fr/hebergement-donnees-de-sante/) and you can now deploy on `parhds` zone from the [API](/api/), [Clever Tools](/doc/cli/) or the [Console](https://console.clever-cloud.com). To benefit from certified hosting for health data, you need to deploy in an HDS zone and to sign up to a specific contract. This begins with [an initial discussion with our team](https://www.clever.cloud/fr/hebergement-donnees-de-sante/contact-hds/). -* [Learn more about Clever Cloud HDS certification](https://www.clever.cloud/fr/hebergement-donnees-de-sante/) -* [Learn more about HDS hosting on Clever Cloud](https://www.clever.cloud/fr/blog/entreprise/2025/03/19/cloud-hds-avec-clever-cloud/) +- [Learn more about Clever Cloud HDS certification](https://www.clever.cloud/fr/hebergement-donnees-de-sante/) +- [Learn more about HDS hosting on Clever Cloud](https://www.clever.cloud/fr/blog/entreprise/2025/03/19/cloud-hds-avec-clever-cloud/) diff --git a/content/changelog/2025/03-19-terraform-0.7-resources-update.md b/content/changelog/2025/03-19-terraform-0.7-resources-update.md index 4b30de072..bb7073d15 100644 --- a/content/changelog/2025/03-19-terraform-0.7-resources-update.md +++ b/content/changelog/2025/03-19-terraform-0.7-resources-update.md @@ -17,4 +17,4 @@ excludeSearch: true With [0.6.0 release](/changelog/2025/02-19-terraform-0.6-redis/) of the Clever Cloud Terraform provider (compatible with OpenTofu), we introduced the support of resource update. Starting with [0.7.0](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v0.7.0) you can now apply a new plan without destroying and recreating the resource on Docker, Java, Python, Scala and Static applications. -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/03-21-php-version-management-update.md b/content/changelog/2025/03-21-php-version-management-update.md index df231b069..7a4931824 100644 --- a/content/changelog/2025/03-21-php-version-management-update.md +++ b/content/changelog/2025/03-21-php-version-management-update.md @@ -16,9 +16,9 @@ Starting today, PHP applications on Clever Cloud are created with `CC_PHP_VERSIO Starting april 30th, the default minor PHP version for branch `8` will move from `8.3` to `8.4`, released last November. If you need to keep using PHP 8.3, just set `CC_PHP_VERSION=8.3` in your environment variables from the Console or with Clever Tools: -``` +```bash clever env set CC_PHP_VERSION 8.3 --app ``` -* [Learn more about PHP on Clever Cloud](/doc/applications/php/) -* [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) +- [Learn more about PHP on Clever Cloud](/doc/applications/php/) +- [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) diff --git a/content/changelog/2025/03-24-postgresql-11-12-eol.md b/content/changelog/2025/03-24-postgresql-11-12-eol.md index cb27065ed..7ac50adcc 100644 --- a/content/changelog/2025/03-24-postgresql-11-12-eol.md +++ b/content/changelog/2025/03-24-postgresql-11-12-eol.md @@ -13,10 +13,11 @@ excludeSearch: true --- As [PostgreSQL 16 and 17 are now available](/changelog/2025/03-18-postgresql-16-17/) on Clever Cloud, we'll start to limit access to end-of-life releases: PostgreSQL 11 and 12. **Starting April 30th**, you won't be able to create new PostgreSQL 11 or 12 add-ons. Note that: -* PG11/12 databases deployed before April 30th will still be available after this deadline -* Customers will still be able to manage their existing PG11/12 databases after this deadline + +- PG11/12 databases deployed before April 30th will still be available after this deadline +- Customers will still be able to manage their existing PG11/12 databases after this deadline But **we recommend you to migrate to an actively supported version**. You can do it easily using our included migration tool in your add-on parameters. In the coming weeks, you'll start to see warnings in [the Console](https://console.clever-cloud.com) if you are still using PostgreSQL 12 or a lower version. -* [PostgreSQL versioning policy](https://www.postgresql.org/support/versioning/) -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [PostgreSQL versioning policy](https://www.postgresql.org/support/versioning/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2025/03-25-python-3.8-eol.md b/content/changelog/2025/03-25-python-3.8-eol.md index c33c50141..29231aad0 100644 --- a/content/changelog/2025/03-25-python-3.8-eol.md +++ b/content/changelog/2025/03-25-python-3.8-eol.md @@ -18,5 +18,5 @@ To upgrade to a more recent version of Python, you only need to update `CC_PYTHO If you need to keep Python 3.8 for legacy reasons, you can [use uv](https://docs.astral.sh/uv/guides/install-python/#installing-a-specific-version) or [Mise](https://mise.jdx.dev/lang/python.html) which are available in all our recently updated images. -* [Learn more about Python on Clever Cloud](/doc/applications/python/) -* [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) +- [Learn more about Python on Clever Cloud](/doc/applications/python/) +- [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) diff --git a/content/changelog/2025/03-27-postgresql-default.md b/content/changelog/2025/03-27-postgresql-default.md index 0eded7e71..b6563b0ee 100644 --- a/content/changelog/2025/03-27-postgresql-default.md +++ b/content/changelog/2025/03-27-postgresql-default.md @@ -14,5 +14,5 @@ excludeSearch: true As [PostgreSQL 16 and 17 are available](/changelog/2025/03-18-postgresql-16-17/) on Clever Cloud, we start using PostgreSQL 17 as the default version for new add-ons. **Starting today**, when you create a new PostgreSQL database, it will be setup with PostgreSQL 17. -* [PostgreSQL versioning policy](https://www.postgresql.org/support/versioning/) -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [PostgreSQL versioning policy](https://www.postgresql.org/support/versioning/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2025/03-28-frankenphp-runtime.md b/content/changelog/2025/03-28-frankenphp-runtime.md index 4394bc39c..9669914ac 100644 --- a/content/changelog/2025/03-28-frankenphp-runtime.md +++ b/content/changelog/2025/03-28-frankenphp-runtime.md @@ -33,6 +33,6 @@ FrankenPHP on Clever Cloud comes with [lots of extensions](/doc/applications/fra As it's a new image, help us improve it by reporting any issue or suggestion on the [Clever Cloud Community](https://github.com/CleverCloud/Community/discussions/categories/frankenphp). -* [FrankenPHP and Materia KV example](https://github.com/CleverCloud/frankenphp-kv-json-example) -* [Learn more about FrankenPHP on Clever Cloud](/doc/applications/frankenphp/) -* [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) +- [FrankenPHP and Materia KV example](https://github.com/CleverCloud/frankenphp-kv-json-example) +- [Learn more about FrankenPHP on Clever Cloud](/doc/applications/frankenphp/) +- [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) diff --git a/content/changelog/2025/04-01-images-update.md b/content/changelog/2025/04-01-images-update.md index 19bac28e9..be392e828 100644 --- a/content/changelog/2025/04-01-images-update.md +++ b/content/changelog/2025/04-01-images-update.md @@ -14,10 +14,10 @@ excludeSearch: true We deployed and updated all our images, except Docker and Java, with no impact for our users. -* **Common:** - * Linux kernel 6.13.8 - * Mise 2025.3.11 -* **Python:** - * uv 0.6.11 -* **Rust:** - * Update to 1.85.1 +- **Common:** + - Linux kernel 6.13.8 + - Mise 2025.3.11 +- **Python:** + - uv 0.6.11 +- **Rust:** + - Update to 1.85.1 diff --git a/content/changelog/2025/04-02-postgresql-replicas.md b/content/changelog/2025/04-02-postgresql-replicas.md index ca8ba8fa0..4df8ea6bd 100644 --- a/content/changelog/2025/04-02-postgresql-replicas.md +++ b/content/changelog/2025/04-02-postgresql-replicas.md @@ -16,5 +16,5 @@ PostgreSQL dashboard in [the Clever Cloud Console](https://console.clever-cloud. To set up replicas, ask your sales representative or [contact support](https://console.clever-cloud.com/ticket-center-choice). -* [Learn more about PostgreSQL replication](/doc/addons/postgresql#replication) -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [Learn more about PostgreSQL replication](/doc/addons/postgresql#replication) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2025/04-03-images-update.md b/content/changelog/2025/04-03-images-update.md index f41cfcc35..5ac40c300 100644 --- a/content/changelog/2025/04-03-images-update.md +++ b/content/changelog/2025/04-03-images-update.md @@ -14,13 +14,13 @@ excludeSearch: true We deployed and updated all our images, with no impact for our users. -* **Common:** - * Fix an issue with scaling feature - * Linux kernel 6.13.9 - * Mise 2025.3.11 - * Tailscale 1.82.0 -* **Go:** - * Update to 1.24.2 +- **Common:** + - Fix an issue with scaling feature + - Linux kernel 6.13.9 + - Mise 2025.3.11 + - Tailscale 1.82.0 +- **Go:** + - Update to 1.24.2 SQL Server extensions (`sqlsrv` and `pdo_sqlsrv`) are now available for PHP 8.1 to 8.4. To enable them, add `ENABLE_SQLSRV=true` or `ENABLE_PDO_SQLSRV=true` environment variables to your application. diff --git a/content/changelog/2025/04-04-postgresql-locale.md b/content/changelog/2025/04-04-postgresql-locale.md index e632b839e..12f5cfe3e 100644 --- a/content/changelog/2025/04-04-postgresql-locale.md +++ b/content/changelog/2025/04-04-postgresql-locale.md @@ -20,5 +20,5 @@ clever addon create postgresql-addon pgLocaleTest --plan xxs_sml --option locale This will create a PostgreSQL database with the `en_US` locale instead of the default `en_GB` locale. -* [PostgreSQL versioning policy](https://www.postgresql.org/support/versioning/) -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [PostgreSQL versioning policy](https://www.postgresql.org/support/versioning/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2025/04-10-mysql-8.4.4-update.md b/content/changelog/2025/04-10-mysql-8.4.4-update.md index 4cfba047d..285bced84 100644 --- a/content/changelog/2025/04-10-mysql-8.4.4-update.md +++ b/content/changelog/2025/04-10-mysql-8.4.4-update.md @@ -14,5 +14,5 @@ excludeSearch: true We updated MySQL 8.4 from Percona Server. They're now available for add-ons creation and migration. -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) -* [Learn more about MySQL 8.4.4-4](https://docs.percona.com/percona-server/8.4/release-notes/8.4.4-4.html) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.4.4-4](https://docs.percona.com/percona-server/8.4/release-notes/8.4.4-4.html) diff --git a/content/changelog/2025/04-16-images-update.md b/content/changelog/2025/04-16-images-update.md index a43eb4c71..17d938cf6 100644 --- a/content/changelog/2025/04-16-images-update.md +++ b/content/changelog/2025/04-16-images-update.md @@ -14,21 +14,21 @@ excludeSearch: true We updated all our images, except Java. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.14.2 - * Mise 2025.4.2 - * OpenSSH 10.0p2 - * OpenSSL 3.5.0 - * Tailscale 1.82.0 -* **Node.js:** - * Bun 1.2.9 -* **PHP:** - * Update to 8.4.6 - * Composer 2.8.8 -* **Python:** - * Update to 3.13.3 - * uv 0.6.14 -* **Rust:** - * Update to 1.86.0 +- **Common:** + - Linux kernel 6.14.2 + - Mise 2025.4.2 + - OpenSSH 10.0p2 + - OpenSSL 3.5.0 + - Tailscale 1.82.0 +- **Node.js:** + - Bun 1.2.9 +- **PHP:** + - Update to 8.4.6 + - Composer 2.8.8 +- **Python:** + - Update to 3.13.3 + - uv 0.6.14 +- **Rust:** + - Update to 1.86.0 Bun is now part of our included tools, and will be natively supported in an upcoming release of our Node.js runtime. diff --git a/content/changelog/2025/04-24-keycloak-26.2.1-grafana.md b/content/changelog/2025/04-24-keycloak-26.2.1-grafana.md index 709277f1e..4725e7db9 100644 --- a/content/changelog/2025/04-24-keycloak-26.2.1-grafana.md +++ b/content/changelog/2025/04-24-keycloak-26.2.1-grafana.md @@ -20,6 +20,7 @@ The release `26.2.1` of Keycloak is available on Clever Cloud. [This new branch ## Grafana dashboard Each Clever Cloud account organisation [comes with a pre-configured Grafana service](/doc/metrics/#publish-your-own-metrics). With recent Keycloak enhancements on metrics and observability, we've worked on a Grafana dashboard ready to import: + - Go to the `Metrics in Grafana` section of your organisation or personal space in [Console](https://console.clever-cloud.com/) - Open Grafana, click on the `+` icon in the upper right corner and select `Import` dashboard - Import this [JSON file](https://cc-keycloak.cellar-c2.services.clever-cloud.com/keycloak-grafana-dashboard.json) diff --git a/content/changelog/2025/05-06-images-update.md b/content/changelog/2025/05-06-images-update.md index 52e08baac..7d36ff44c 100644 --- a/content/changelog/2025/05-06-images-update.md +++ b/content/changelog/2025/05-06-images-update.md @@ -14,17 +14,17 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.14.4 - * Mise 2025.5.0 - * Redis 8.0.0 - * Tailscale 1.82.5 -* **Go:** - * Update to 1.24.2 -* **Node.js:** - * Update to 22.15 (npm 10.9.2) -* **Python:** - * uv 0.7.2 +- **Common:** + - Linux kernel 6.14.4 + - Mise 2025.5.0 + - Redis 8.0.0 + - Tailscale 1.82.5 +- **Go:** + - Update to 1.24.2 +- **Node.js:** + - Update to 22.15 (npm 10.9.2) +- **Python:** + - uv 0.7.2 As previously announced, Python 3.8 [is not available in our images anymore](/changelog/2025/03-25-python-3.8-eol/) and PHP 8.4 [is now used as default](/changelog/2025/03-21-php-version-management-update/) for `CC_PHP_VERSION=8`. As Node.js 18 and Ruby 3.1 are now end-of-life, you'll get a warning if you use them. diff --git a/content/changelog/2025/05-07-node-24.md b/content/changelog/2025/05-07-node-24.md index 2e106d832..4d4a20513 100644 --- a/content/changelog/2025/05-07-node-24.md +++ b/content/changelog/2025/05-07-node-24.md @@ -13,6 +13,7 @@ excludeSearch: true --- 2025 is an important year for Node.js ecosystem with big changes for multiple branches: + - 18.x LTS is end-of-life since April 30th and won't receive any new security updates - 20.x LTS is in maintenance mode and will receive security updates until April 30th, 2026 - 22.x LTS, will enter maintenance mode in October and will receive security updates until April 30th, 2027 @@ -21,6 +22,6 @@ excludeSearch: true In Clever Cloud runtimes, we use the latest LTS version by default. Thus, it's currently 22.x, and we'll move to 24.x in October. Until then you can use 24.x branch by setting the `CC_NODE_VERSION` environment variable to `^24`, `24` or `24.0`, for example: -* [Learn more about Node.js release cycle](https://nodejs.org/en/about/releases/) -* [Learn more about Node.js on Clever Cloud](/doc/applications/nodejs/) -* [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) +- [Learn more about Node.js release cycle](https://nodejs.org/en/about/releases/) +- [Learn more about Node.js on Clever Cloud](/doc/applications/nodejs/) +- [Learn more about environment variables on Clever Cloud](/doc/reference/reference-environment-variables/) diff --git a/content/changelog/2025/05-13-images-update.md b/content/changelog/2025/05-13-images-update.md index 984d081b9..f7a3e8257 100644 --- a/content/changelog/2025/05-13-images-update.md +++ b/content/changelog/2025/05-13-images-update.md @@ -14,18 +14,18 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.14.6 -* **Docker:** - * Update to 28.11.1 -* **Elixir** - * Erlang 25.3.2.21 - * Erlang 26.2.5.12 - * Erlang 27.3.4 -* **Go:** - * Update to 1.24.3 -* **PHP:** - * Update to 8.3.21 - * Update to 8.4.7 -* **Python:** - * uv 0.7.3 +- **Common:** + - Linux kernel 6.14.6 +- **Docker:** + - Update to 28.11.1 +- **Elixir** + - Erlang 25.3.2.21 + - Erlang 26.2.5.12 + - Erlang 27.3.4 +- **Go:** + - Update to 1.24.3 +- **PHP:** + - Update to 8.3.21 + - Update to 8.4.7 +- **Python:** + - uv 0.7.3 diff --git a/content/changelog/2025/05-16-images-update.md b/content/changelog/2025/05-16-images-update.md index 0f9241182..feff6ff25 100644 --- a/content/changelog/2025/05-16-images-update.md +++ b/content/changelog/2025/05-16-images-update.md @@ -14,23 +14,23 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.14.6 with stability fix - * Mise 2025.5.3 - * Redis 8.0.1 - * SQLite 3.49.2 -* **Node.js:** - * Update to 22.15.1 (npm 10.9.2) - * Bun 1.2.9 -* **PHP:** - * Composer 2.8.9 -* **Ruby:** - * Update to 3.1.7 - * Update to 3.2.8 - * Update to 3.3.8 - * Update to 3.4.3 -* **Rust:** - * Update to 1.87.0 +- **Common:** + - Linux kernel 6.14.6 with stability fix + - Mise 2025.5.3 + - Redis 8.0.1 + - SQLite 3.49.2 +- **Node.js:** + - Update to 22.15.1 (npm 10.9.2) + - Bun 1.2.9 +- **PHP:** + - Composer 2.8.9 +- **Ruby:** + - Update to 3.1.7 + - Update to 3.2.8 + - Update to 3.3.8 + - Update to 3.4.3 +- **Rust:** + - Update to 1.87.0 This release introduces `CC_NODE_VERSION` support [for Node.js applications](/changelog/2025/05-16-node-versions/). When an application is restarted for image update, it won't appear as `Maintenance` anymore, but as `Maintenance/ImageUpdate` diff --git a/content/changelog/2025/05-16-terraform-0.8.md b/content/changelog/2025/05-16-terraform-0.8.md index 8e6411352..3838ddeb9 100644 --- a/content/changelog/2025/05-16-terraform-0.8.md +++ b/content/changelog/2025/05-16-terraform-0.8.md @@ -17,4 +17,4 @@ excludeSearch: true The [0.8.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v0.8.0) of the Clever Cloud Terraform provider introduces support for [FS Buckets](/doc/addons/fs-bucket/) and update for [Cellar add-ons](/doc/addons/cellar/) -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/05-20-pg-update-17.5.md b/content/changelog/2025/05-20-pg-update-17.5.md index 92b5aa2fc..3e8c0b7f2 100644 --- a/content/changelog/2025/05-20-pg-update-17.5.md +++ b/content/changelog/2025/05-20-pg-update-17.5.md @@ -13,12 +13,13 @@ excludeSearch: true --- New PostgreSQL versions are available for new add-ons and migration: -* PostgreSQL 17.5 -* PostgreSQL 16.9 -* PostgreSQL 15.13 -* PostgreSQL 14.18 -* PostgreSQL 13.21 + +- PostgreSQL 17.5 +- PostgreSQL 16.9 +- PostgreSQL 15.13 +- PostgreSQL 14.18 +- PostgreSQL 13.21 It includes [fixes and improvements](https://www.postgresql.org/about/news/postgresql-175-169-1513-1418-and-1321-released-3072/), including a security patch for [CVE-2025-4207](https://nvd.nist.gov/vuln/detail/CVE-2025-4207). -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2025/06-03-images-update.md b/content/changelog/2025/06-03-images-update.md index 80e969bc0..8b71a335f 100644 --- a/content/changelog/2025/06-03-images-update.md +++ b/content/changelog/2025/06-03-images-update.md @@ -14,32 +14,34 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.14.9 - * Mise 2025.5.17 - * Redis 8.0.2 - * SQLite 3.50.0 -* **Elixir:** - * Update to 1.18.4 - * Erlang 28.0 -* **FrankenPHP:** - * Update to 1.6.2 -* **Go:** - * Update to 1.24.3 -* **Node.js:** - * Update to 22.16.0 (npm 10.9.2) - * Bun 1.2.15 -* **Python:** - * uv 0.7.9 -* **Ruby:** - * Update to 3.4.4 +- **Common:** + - Linux kernel 6.14.9 + - Mise 2025.5.17 + - Redis 8.0.2 + - SQLite 3.50.0 +- **Elixir:** + - Update to 1.18.4 + - Erlang 28.0 +- **FrankenPHP:** + - Update to 1.6.2 +- **Go:** + - Update to 1.24.3 +- **Node.js:** + - Update to 22.16.0 (npm 10.9.2) + - Bun 1.2.15 +- **Python:** + - uv 0.7.9 +- **Ruby:** + - Update to 3.4.4 ## Bun native support + This release introduces [Bun](https://bun.sh) native support on Clever Cloud, with auto-detection of `bun.lock` file. - [Learn more about Bun support on Clever Cloud](/changelog/2025/06-03-native-bun-support/) ## FrankenPHP custom port + You can also set `CC_FRANKENPHP_PORT` to use a different port than the default `8080`. It allows you to use services in front of your FrankenPHP application such as [Redirection.io](/doc/reference/reference-environment-variables/#use-redirectionio-as-a-proxy) for example. - [Learn more about FrankenPHP on Clever Cloud](/doc/applications/frankenphp/) diff --git a/content/changelog/2025/06-03-native-bun-support.md b/content/changelog/2025/06-03-native-bun-support.md index 4e13868ac..12655026e 100644 --- a/content/changelog/2025/06-03-native-bun-support.md +++ b/content/changelog/2025/06-03-native-bun-support.md @@ -18,7 +18,7 @@ If a `bun.lock` file is detected at the root of your project, and you didn't ask Want to give it a try? Just launch these commands with Bun and [Clever Tools CLI](/doc/cli/install) installed on your system: -``` +```bash mkdir bunDemo cd bunDemo @@ -35,4 +35,4 @@ clever open You'll get a basic React application running on Bun, deployed on Clever Cloud. -* [Learn more about Bun & Node.js on Clever Cloud](/doc/applications/nodejs) +- [Learn more about Bun & Node.js on Clever Cloud](/doc/applications/nodejs) diff --git a/content/changelog/2025/06-03-terraform-0.9.md b/content/changelog/2025/06-03-terraform-0.9.md index 1be536d9e..4e662f3ab 100644 --- a/content/changelog/2025/06-03-terraform-0.9.md +++ b/content/changelog/2025/06-03-terraform-0.9.md @@ -17,4 +17,4 @@ excludeSearch: true The [0.9.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v0.9.0) of the Clever Cloud Terraform provider introduces support for `play2` applications and git local repositories. Starting with this release, if you change an application environment variable, it will be restarted automatically. -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/06-04-otoroshi-17.3.md b/content/changelog/2025/06-04-otoroshi-17.3.md index 8fd608e1a..8660731cf 100644 --- a/content/changelog/2025/06-04-otoroshi-17.3.md +++ b/content/changelog/2025/06-04-otoroshi-17.3.md @@ -31,4 +31,3 @@ clever otoroshi version update yourOtoroshiNameOrId v17.3.1_1749049547 - [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/docs/topics/workflows) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) - diff --git a/content/changelog/2025/06-05-api-tokens-console-tips.md b/content/changelog/2025/06-05-api-tokens-console-tips.md index a05534d58..6bc6d22c6 100644 --- a/content/changelog/2025/06-05-api-tokens-console-tips.md +++ b/content/changelog/2025/06-05-api-tokens-console-tips.md @@ -24,7 +24,7 @@ This interface also shows the token name, description, public ID, creation and e To create an API token, you need to provide: - Your current password -- Your 2FA code if active (activate it [here](https://console.clever-cloud.com/users/me/authentication)) +- Your 2FA code if active ([activate 2FA in the Console](https://console.clever-cloud.com/users/me/authentication)) If you created your Clever Cloud account through the GitHub integration, you'll be asked to set a password first. diff --git a/content/changelog/2025/06-05-mysql-8.0.42-8.4.5.md b/content/changelog/2025/06-05-mysql-8.0.42-8.4.5.md index d0a247eb9..79f2575a7 100644 --- a/content/changelog/2025/06-05-mysql-8.0.42-8.4.5.md +++ b/content/changelog/2025/06-05-mysql-8.0.42-8.4.5.md @@ -14,6 +14,6 @@ excludeSearch: true We updated MySQL 8.0 and 8.4 from Percona Server. They're now available for add-ons creation and migration. -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) -* [Learn more about MySQL 8.0.42](https://docs.percona.com/percona-server/8.0/release-notes/8.0.42-33.html) -* [Learn more about MySQL 8.4.5](https://docs.percona.com/percona-server/8.4/release-notes/8.4.5-5.html) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.0.42](https://docs.percona.com/percona-server/8.0/release-notes/8.0.42-33.html) +- [Learn more about MySQL 8.4.5](https://docs.percona.com/percona-server/8.4/release-notes/8.4.5-5.html) diff --git a/content/changelog/2025/06-10-clever-tools-3.13.md b/content/changelog/2025/06-10-clever-tools-3.13.md index c9fcfd670..925c5bb1d 100644 --- a/content/changelog/2025/06-10-clever-tools-3.13.md +++ b/content/changelog/2025/06-10-clever-tools-3.13.md @@ -44,7 +44,7 @@ clever otoroshi version update otoroshi_id Manage your Clever Cloud account's emails and public SSH keys with the new `clever emails` and `clever ssh-keys` commands: -``` +```console clever emails clever ssh-keys -F json @@ -62,7 +62,7 @@ clever ssh-keys remove-all You can also use `clever config` to set an application as a [Clever Task](/doc/develop/tasks/) after its creation of get this option configuration: -``` +```console clever config get task clever config set task true clever config set task false @@ -75,7 +75,7 @@ clever config update --name "My new task name" --enable-task To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/06-17-images-update.md b/content/changelog/2025/06-17-images-update.md index 7d487c97d..05be345cf 100644 --- a/content/changelog/2025/06-17-images-update.md +++ b/content/changelog/2025/06-17-images-update.md @@ -14,25 +14,25 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.14.11 - * Mise 2025.6.4 -* **Docker:** - * Update to 28.2.2 - * Buildx 0.24.0 -* **FrankenPHP:** - * Update to 1.7.0 (PHP 8.4.8) -* **Go:** - * Update to 1.24.4 -* **Node.js:** - * Bun 1.2.16 -* **Python:** - * Update to 3.13.4 - * Update to 3.12.11 - * Update to 3.11.13 - * Update to 3.10.18 - * Update to 3.9.23 - * uv 0.7.13 +- **Common:** + - Linux kernel 6.14.11 + - Mise 2025.6.4 +- **Docker:** + - Update to 28.2.2 + - Buildx 0.24.0 +- **FrankenPHP:** + - Update to 1.7.0 (PHP 8.4.8) +- **Go:** + - Update to 1.24.4 +- **Node.js:** + - Bun 1.2.16 +- **Python:** + - Update to 3.13.4 + - Update to 3.12.11 + - Update to 3.11.13 + - Update to 3.10.18 + - Update to 3.9.23 + - uv 0.7.13 ## Other changes diff --git a/content/changelog/2025/07-01-images-update.md b/content/changelog/2025/07-01-images-update.md index 8dc2db02b..830356ff1 100644 --- a/content/changelog/2025/07-01-images-update.md +++ b/content/changelog/2025/07-01-images-update.md @@ -14,23 +14,23 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.14.11 - * Mise 2025.6.6 - * SQLite 3.50.1 -* **Docker:** - * Update to 28.3 - * Buildx 0.25.0 -* **Node.js:** - * Update to 22.17.0 - * Bun 1.2.17 -* **Python:** - * Update to 3.13.5 - * uv 0.7.17 -* **Rust:** - * Update to 1.88.0 -* **V:** - * Update to 0.4.11 +- **Common:** + - Linux kernel 6.14.11 + - Mise 2025.6.6 + - SQLite 3.50.1 +- **Docker:** + - Update to 28.3 + - Buildx 0.25.0 +- **Node.js:** + - Update to 22.17.0 + - Bun 1.2.17 +- **Python:** + - Update to 3.13.5 + - uv 0.7.17 +- **Rust:** + - Update to 1.88.0 +- **V:** + - Update to 0.4.11 ## Linux, Static and V runtimes diff --git a/content/changelog/2025/07-02-terraform-0.10.md b/content/changelog/2025/07-02-terraform-0.10.md index 6165a8b2b..4685b2168 100644 --- a/content/changelog/2025/07-02-terraform-0.10.md +++ b/content/changelog/2025/07-02-terraform-0.10.md @@ -17,4 +17,4 @@ excludeSearch: true The [0.10.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v0.10.0) of the Clever Cloud Terraform provider is available. It fixes some bugs, introduces support for [Network Groups](/doc/develop/network-groups/) and [Pulsar](/doc/addons/pulsar/). You can set a version for a [PostgreSQL add-on](/doc/addons/postgresql/). -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/07-04-linux-static-v-runtimes.md b/content/changelog/2025/07-04-linux-static-v-runtimes.md index 75ea11349..15008cf32 100644 --- a/content/changelog/2025/07-04-linux-static-v-runtimes.md +++ b/content/changelog/2025/07-04-linux-static-v-runtimes.md @@ -15,12 +15,13 @@ excludeSearch: true --- You can now deploy your applications using the new `linux`, `static` and `v` runtimes on Clever Cloud: + - `linux` is a general purpose runtime that supports any language, and fully takes advantage of the [Mise package manager](https://mise.jdx.dev/) - `static` is a lightweight runtime for static sites, with auto-build for many static site generators (SSG) - `v` is a runtime for applications written in the [V programming language](https://vlang.io/) As they are new runtimes, help us improve it by reporting any issue or suggestion on the [Clever Cloud Community](https://github.com/CleverCloud/Community/discussions/categories/paas-runtimes). -* [Learn more about the Linux runtime](/doc/applications/linux/) -* [Learn more about the Static runtime](/doc/applications/static/) -* [Learn more about the V runtime](/doc/applications/v/) +- [Learn more about the Linux runtime](/doc/applications/linux/) +- [Learn more about the Static runtime](/doc/applications/static/) +- [Learn more about the V runtime](/doc/applications/v/) diff --git a/content/changelog/2025/07-08-terraform-0.11.md b/content/changelog/2025/07-08-terraform-0.11.md index 2f0c4b3f9..2e06db28e 100644 --- a/content/changelog/2025/07-08-terraform-0.11.md +++ b/content/changelog/2025/07-08-terraform-0.11.md @@ -17,4 +17,4 @@ excludeSearch: true The [0.11.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v0.11.0) of the Clever Cloud Terraform provider is available. It allows disabling backups for a [PostgreSQL add-on](/doc/addons/postgresql/). -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/07-09-images-update.md b/content/changelog/2025/07-09-images-update.md index ddea8ce80..c97cc29f8 100644 --- a/content/changelog/2025/07-09-images-update.md +++ b/content/changelog/2025/07-09-images-update.md @@ -14,27 +14,27 @@ excludeSearch: true We updated all our images, except PHP with Apache. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.15.5 - * Hugo 0.148.0 - * Mise 2025.7.1 - * SQLite 3.50.2 - * Redis 8.0.3 -* **Docker:** - * Update to 28.3.1 -* **FrankenPHP:** - * Update to 1.8.0 (PHP 8.4.10, Caddy 2.10.0) -* **Java:** - * Now part of the unified image - * Java 24 support - * GraalVM CE 23.0.2 (Java 17) - * GraalVM CE 23.1.2 (Java 21) - * Gradle 8.14.3 - * Maven 3.9.10 -* **Node.js:** - * Bun 1.2.18 -* **Python:** - * uv 0.7.19 +- **Common:** + - Linux kernel 6.15.5 + - Hugo 0.148.0 + - Mise 2025.7.1 + - SQLite 3.50.2 + - Redis 8.0.3 +- **Docker:** + - Update to 28.3.1 +- **FrankenPHP:** + - Update to 1.8.0 (PHP 8.4.10, Caddy 2.10.0) +- **Java:** + - Now part of the unified image + - Java 24 support + - GraalVM CE 23.0.2 (Java 17) + - GraalVM CE 23.1.2 (Java 21) + - Gradle 8.14.3 + - Maven 3.9.10 +- **Node.js:** + - Bun 1.2.18 +- **Python:** + - uv 0.7.19 ## FrankenPHP, Linux, Static and V runtimes diff --git a/content/changelog/2025/07-16-otoroshi-17.4.md b/content/changelog/2025/07-16-otoroshi-17.4.md index f82ce2289..2786db03a 100644 --- a/content/changelog/2025/07-16-otoroshi-17.4.md +++ b/content/changelog/2025/07-16-otoroshi-17.4.md @@ -29,4 +29,3 @@ clever otoroshi version update yourOtoroshiNameOrId v17.4.0_1752074416 - [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/docs/topics/workflows) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) - diff --git a/content/changelog/2025/07-17-request-flow.md b/content/changelog/2025/07-17-request-flow.md index 557959c85..586424f08 100644 --- a/content/changelog/2025/07-17-request-flow.md +++ b/content/changelog/2025/07-17-request-flow.md @@ -23,6 +23,7 @@ Clever Cloud exists to ease developers' life. For many years, you can use [Varni ## Easier Redirection.io and Varnish activation First, we've removed some barriers to use Redirection.io and Varnish: + - To setup Varnish add a `clevercloud/varnish.vcl` file or define it with `CC_VARNISH_FILE` environment variable - To setup Redirection.io, `CC_REDIRECTIONIO_PROJECT_KEY` is now the only required environment variable @@ -31,6 +32,7 @@ First, we've removed some barriers to use Redirection.io and Varnish: In recent runtimes where Clever Cloud manage the port configuration (`frankenphp`, `static`), nothing else is needed. By default, Clever Cloud will configure the reverse proxy to listen on port `8080` and redirect to port `9000`. So in runtimes where the user controls the port configuration, the application must listen on port `9000` once Redirection.io or Varnish is activated. If both are activated: + - Varnish is exposed first: listens on port `8080`, forward to Redirection.io on port `8081` - Redirection.io listens on port `8081`, forward to the application on port `9000` diff --git a/content/changelog/2025/07-29-clever-tools-3.14.md b/content/changelog/2025/07-29-clever-tools-3.14.md index afcef40fc..f6304f814 100644 --- a/content/changelog/2025/07-29-clever-tools-3.14.md +++ b/content/changelog/2025/07-29-clever-tools-3.14.md @@ -30,7 +30,7 @@ npm exec -- clever-tools@3.14 profile --format json To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/07-29-images-update.md b/content/changelog/2025/07-29-images-update.md index 9daa404f3..962b4f396 100644 --- a/content/changelog/2025/07-29-images-update.md +++ b/content/changelog/2025/07-29-images-update.md @@ -14,41 +14,41 @@ excludeSearch: true We updated all our images, except Node.js & Bun. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.15.8 - * Mise 2025.7.29 - * Tailscale 1.86.0 -* **Docker:** - * Update to 28.3.2 - * Buildx 0.26.1 -* **Elixir:** - * Update to 27.3.4.2 - * Update to 28.0.2 -* **Go:** - * Update to 1.24.5 -* **Java:** - * Update to 8.0.462 - * Update to 11.0.28 - * Update to 17.0.16 - * Update to 21.0.8 - * Update to 24.0.2 - * Maven 3.9.11 -* **FrankenPHP:** - * Update to 1.9.0 (PHP 8.4.10, Caddy v2.10.0) -* **PHP:** - * Update to 8.1.33 - * Update to 8.2.29 - * Update to 8.3.23 - * Update to 8.4.10 - * Apache 2.4.64 - * Composer 2.8.10 -* **Python:** - * uv 0.8.3 -* **Ruby:** - * Update to 3.2.9 - * Update to 3.3.9 - * Update to 3.4.5 -* **Static:** - * Hugo 0.148.2 - * Static Web Server 2.38.0 - * Zola 0.21.0 +- **Common:** + - Linux kernel 6.15.8 + - Mise 2025.7.29 + - Tailscale 1.86.0 +- **Docker:** + - Update to 28.3.2 + - Buildx 0.26.1 +- **Elixir:** + - Update to 27.3.4.2 + - Update to 28.0.2 +- **Go:** + - Update to 1.24.5 +- **Java:** + - Update to 8.0.462 + - Update to 11.0.28 + - Update to 17.0.16 + - Update to 21.0.8 + - Update to 24.0.2 + - Maven 3.9.11 +- **FrankenPHP:** + - Update to 1.9.0 (PHP 8.4.10, Caddy v2.10.0) +- **PHP:** + - Update to 8.1.33 + - Update to 8.2.29 + - Update to 8.3.23 + - Update to 8.4.10 + - Apache 2.4.64 + - Composer 2.8.10 +- **Python:** + - uv 0.8.3 +- **Ruby:** + - Update to 3.2.9 + - Update to 3.3.9 + - Update to 3.4.5 +- **Static:** + - Hugo 0.148.2 + - Static Web Server 2.38.0 + - Zola 0.21.0 diff --git a/content/changelog/2025/07-29-terraform-1.0.md b/content/changelog/2025/07-29-terraform-1.0.md index e031d2474..67fe70bcf 100644 --- a/content/changelog/2025/07-29-terraform-1.0.md +++ b/content/changelog/2025/07-29-terraform-1.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.0.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.0.0) of the Clever Cloud Terraform provider is available. It fixes some bugs, supports [Rust runtime](/doc/applications/rust/) and uses real ID instead of add-on ID for add-ons. -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/08-07-images-update.md b/content/changelog/2025/08-07-images-update.md index d4303fba0..b9c31ec7f 100644 --- a/content/changelog/2025/08-07-images-update.md +++ b/content/changelog/2025/08-07-images-update.md @@ -14,19 +14,19 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.15.9 - * Mise 2025.8.4 - * SQLite 3.50.3 - * Tailscale 1.86.2 -* **.NET:** - * Update to 9.0.108 -* **Docker:** - * Update to 28.3.3 -* **Node.js & Bun:** - * Bun 1.2.19 - * Node.js 22.18.0 (npm 10.9.3) -* **Python:** - * uv 0.8.4 +- **Common:** + - Linux kernel 6.15.9 + - Mise 2025.8.4 + - SQLite 3.50.3 + - Tailscale 1.86.2 +- **.NET:** + - Update to 9.0.108 +- **Docker:** + - Update to 28.3.3 +- **Node.js & Bun:** + - Bun 1.2.19 + - Node.js 22.18.0 (npm 10.9.3) +- **Python:** + - uv 0.8.4 In the [.NET runtime](/doc/applications/dotnet), you can now use `CC_DOTNET_VERSION=9.0`. diff --git a/content/changelog/2025/08-12-images-update.md b/content/changelog/2025/08-12-images-update.md index ec5854bbc..3c31ecaaa 100644 --- a/content/changelog/2025/08-12-images-update.md +++ b/content/changelog/2025/08-12-images-update.md @@ -14,19 +14,19 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Tailscale 1.86.4 -* **Go:** - * Update to 1.24.6 -* **Java:** - * Gradle 9.0.0 -* **Node.js & Bun:** - * Bun 1.2.20 -* **Python:** - * 3.13.6 - * pip 25.2 - * uv 0.8.8 -* **Rust:** - * Update to 1.89.0 +- **Common:** + - Tailscale 1.86.4 +- **Go:** + - Update to 1.24.6 +- **Java:** + - Gradle 9.0.0 +- **Node.js & Bun:** + - Bun 1.2.20 +- **Python:** + - 3.13.6 + - pip 25.2 + - uv 0.8.8 +- **Rust:** + - Update to 1.89.0 In the [Bun & Node.js runtime](/doc/applications/nodejs), you can now set `CC_NODE_BUILD_TOOL` to `pnpm` or `yarn-berry`. diff --git a/content/changelog/2025/08-27-clever-tools-4.0.md b/content/changelog/2025/08-27-clever-tools-4.0.md index a917ddf2c..7618509fb 100644 --- a/content/changelog/2025/08-27-clever-tools-4.0.md +++ b/content/changelog/2025/08-27-clever-tools-4.0.md @@ -18,6 +18,7 @@ excludeSearch: true Clever Tools 4.0 is available. It's a new major version as there are multiple breaking changes. Not in commands nor behavior, but in runtime, code quality and tooling. This release is now based on Node.js 22 and [Clever Cloud JS client 11.0](https://www.npmjs.com/package/@clevercloud/client). Under the hood we've also : + - Updated [CONTRIBUTING](https://github.com/CleverCloud/clever-tools/blob/master/CONTRIBUTING.md) and [README](https://github.com/CleverCloud/clever-tools/blob/master/README.md) files - Removed lots of dependencies, updated those remaining - Added scripts and tools to ease the development and contribution @@ -34,7 +35,7 @@ These changes will pave the way for future major improvements and new features a To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/09-01-metabase-56-default.md b/content/changelog/2025/09-01-metabase-56-default.md index 91443fe13..e71a1cc5b 100644 --- a/content/changelog/2025/09-01-metabase-56-default.md +++ b/content/changelog/2025/09-01-metabase-56-default.md @@ -16,6 +16,7 @@ excludeSearch: true --- The `x.56` branch of Metabase is available on Clever Cloud [since last month](/changelog/2025/08-08-metabase-56/). It's now the default branch deployed with the [release 0.56.3](https://github.com/metabase/metabase/releases/tag/v0.56.3). It means that: + - All new add-ons will use it - All add-ons using default configuration (`community-latest`) will use it after a rebuild diff --git a/content/changelog/2025/09-01-pg-update-17.6.md b/content/changelog/2025/09-01-pg-update-17.6.md index f69809aa2..8ffc81891 100644 --- a/content/changelog/2025/09-01-pg-update-17.6.md +++ b/content/changelog/2025/09-01-pg-update-17.6.md @@ -13,12 +13,13 @@ excludeSearch: true --- New PostgreSQL versions are available for new add-ons and migration: -* PostgreSQL 17.6 -* PostgreSQL 16.10 -* PostgreSQL 15.14 -* PostgreSQL 14.19 -* PostgreSQL 13.22 + +- PostgreSQL 17.6 +- PostgreSQL 16.10 +- PostgreSQL 15.14 +- PostgreSQL 14.19 +- PostgreSQL 13.22 It includes [fixes and improvements](https://www.postgresql.org/about/news/postgresql-176-1610-1514-1419-1322-and-18-beta-3-released-3118/), with security patches for [CVE-2025-8713](https://nvd.nist.gov/vuln/detail/CVE-2025-8713), [CVE-2025-8714](https://nvd.nist.gov/vuln/detail/CVE-2025-8714) and [CVE-2025-8715](https://nvd.nist.gov/vuln/detail/CVE-2025-8715). -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2025/09-09-images-update.md b/content/changelog/2025/09-09-images-update.md index 634d19026..88e823a8e 100644 --- a/content/changelog/2025/09-09-images-update.md +++ b/content/changelog/2025/09-09-images-update.md @@ -14,35 +14,35 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * FFmpeg 8.0 - * Linux kernel 6.15.9 - * Mise 2025.9.6 - * Redis 8.2.1 - * SQLite 3.50.4 - * Varnish 7.7.3 -* **Docker:** - * Update to 28.4.0 - * Buildx 0.28.0 -* **FrankenPHP:** - * Update to 1.9.1 -* **Go:** - * Update to 1.25.1 -* **Node.js & Bun:** - * Update to 22.19.0 (npm 10.9.3) - * Bun 1.2.21 - * Yarn 4.9.4 -* **PHP:** - * Update to PHP 8.3.25 - * Update to PHP 8.4.12 - * Composer 2.2.25 (LTS) - * Composer 2.8.11 -* **Python:** - * 3.13.7 - * uv 0.8.15 -* **Static:** - * Hugo 0.145 removal - * Hugo 0.149.1 +- **Common:** + - FFmpeg 8.0 + - Linux kernel 6.15.9 + - Mise 2025.9.6 + - Redis 8.2.1 + - SQLite 3.50.4 + - Varnish 7.7.3 +- **Docker:** + - Update to 28.4.0 + - Buildx 0.28.0 +- **FrankenPHP:** + - Update to 1.9.1 +- **Go:** + - Update to 1.25.1 +- **Node.js & Bun:** + - Update to 22.19.0 (npm 10.9.3) + - Bun 1.2.21 + - Yarn 4.9.4 +- **PHP:** + - Update to PHP 8.3.25 + - Update to PHP 8.4.12 + - Composer 2.2.25 (LTS) + - Composer 2.8.11 +- **Python:** + - 3.13.7 + - uv 0.8.15 +- **Static:** + - Hugo 0.145 removal + - Hugo 0.149.1 ## Bun 1.2.21: native Clever Cloud add-ons support @@ -50,7 +50,7 @@ The latest Bun 1.2 release [brings native MySQL support](https://bun.com/blog/bu We provide a GitHub repository with ready-to-use examples: -* [Clever Cloud add-ons native Bun support example scripts](https://github.com/CleverCloud/bun-addons-examples) +- [Clever Cloud add-ons native Bun support example scripts](https://github.com/CleverCloud/bun-addons-examples) ## Hugo version update diff --git a/content/changelog/2025/09-10-otoroshi-17.5.md b/content/changelog/2025/09-10-otoroshi-17.5.md index 5b6d26c37..7e17f35a2 100644 --- a/content/changelog/2025/09-10-otoroshi-17.5.md +++ b/content/changelog/2025/09-10-otoroshi-17.5.md @@ -29,4 +29,3 @@ clever otoroshi version update yourOtoroshiNameOrId v17.5.1_1757489873 - [Learn more about Otoroshi Workflows](https://maif.github.io/otoroshi/manual/docs/topics/workflows) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) - diff --git a/content/changelog/2025/09-12-terraform-1.1.0.md b/content/changelog/2025/09-12-terraform-1.1.0.md index 363861103..480a31f2b 100644 --- a/content/changelog/2025/09-12-terraform-1.1.0.md +++ b/content/changelog/2025/09-12-terraform-1.1.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.1.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.1.0) of the Clever Cloud Terraform provider is available. It brings bug fixes, update capability to `frankenphp` and multiple add-ons: `keycloak`, `kv`, `metabase`, `mongodb`, `mysql`, `otoroshi`, `postgresql`, `pulsar`, `redis`. -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/09-16-images-update.md b/content/changelog/2025/09-16-images-update.md index f356f4d8d..36e44d747 100644 --- a/content/changelog/2025/09-16-images-update.md +++ b/content/changelog/2025/09-16-images-update.md @@ -14,28 +14,28 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.16.7 - * Clever Tools 4.0.2 - * Mise 2025.9.9 - * Tailscale 1.88.1 -* **Elixir:** - * Erlang 26.2.5.15 - * Erlang 27.3.4.3 - * Erlang 28.0.3 -* **Node.js & Bun:** - * Bun 1.2.22 -* **PHP:** - * Apache 2.4.65 - * Imagick extension 3.8.0 - * Symfony: fix binary name (was `symfony-cli`, now `symfony`) -* **Python:** - * uv 0.8.17 -* **Static:** - * Caddy 2.10.2 - * Hugo 0.146 removal - * Hugo 0.150.0 - * Static Web Server 2.38.1 +- **Common:** + - Linux kernel 6.16.7 + - Clever Tools 4.0.2 + - Mise 2025.9.9 + - Tailscale 1.88.1 +- **Elixir:** + - Erlang 26.2.5.15 + - Erlang 27.3.4.3 + - Erlang 28.0.3 +- **Node.js & Bun:** + - Bun 1.2.22 +- **PHP:** + - Apache 2.4.65 + - Imagick extension 3.8.0 + - Symfony: fix binary name (was `symfony-cli`, now `symfony`) +- **Python:** + - uv 0.8.17 +- **Static:** + - Caddy 2.10.2 + - Hugo 0.146 removal + - Hugo 0.150.0 + - Static Web Server 2.38.1 ## Hugo version update diff --git a/content/changelog/2025/09-17-clever-tools-4.1.md b/content/changelog/2025/09-17-clever-tools-4.1.md index 8851ce42d..bae27afb4 100644 --- a/content/changelog/2025/09-17-clever-tools-4.1.md +++ b/content/changelog/2025/09-17-clever-tools-4.1.md @@ -16,6 +16,7 @@ excludeSearch: true --- [Clever Tools 4.1.0](https://github.com/CleverCloud/clever-tools/releases/tag/4.1.0) is available. It fixes some bugs and: + - Return application runtime information in `clever status` - Return the local alias after a successful `clever link` command - Add JSON format with details to `clever domain` and `clever domain favourite` commands @@ -24,7 +25,7 @@ excludeSearch: true To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/09-18-mysql-8.0.43-8.4.6.md b/content/changelog/2025/09-18-mysql-8.0.43-8.4.6.md index 3b29c1e21..184d86012 100644 --- a/content/changelog/2025/09-18-mysql-8.0.43-8.4.6.md +++ b/content/changelog/2025/09-18-mysql-8.0.43-8.4.6.md @@ -14,6 +14,6 @@ excludeSearch: true We updated MySQL 8.0 and 8.4 from Percona Server. They're now available for add-ons creation and migration. -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) -* [Learn more about MySQL 8.0.43](https://docs.percona.com/percona-server/8.0/release-notes/8.0.43-34.html) -* [Learn more about MySQL 8.4.6](https://docs.percona.com/percona-server/8.4/release-notes/8.4.6-6.html) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.0.43](https://docs.percona.com/percona-server/8.0/release-notes/8.0.43-34.html) +- [Learn more about MySQL 8.4.6](https://docs.percona.com/percona-server/8.4/release-notes/8.4.6-6.html) diff --git a/content/changelog/2025/09-24-terraform-1.2.0.md b/content/changelog/2025/09-24-terraform-1.2.0.md index b18347354..33a758cf3 100644 --- a/content/changelog/2025/09-24-terraform-1.2.0.md +++ b/content/changelog/2025/09-24-terraform-1.2.0.md @@ -18,7 +18,8 @@ excludeSearch: true The [1.2.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.2.0) of the Clever Cloud Terraform provider is available. It brings multiple bug fixes with some cleaning. It also adds support for Ruby runtime and drains. There is a format change in domains management: -``` + +```text vhosts = [ "api.${var.cc_global_tld}/v4/addon-providers/es-addon", local.api_v2_vhost, @@ -26,11 +27,12 @@ There is a format change in domains management: ``` becomes: -``` + +```text vhosts = [ { fqdn = "api.${var.cc_global_tld}", path_begin = "/v4/addon-providers/es-addon" }, { fqdn = "api.${var.cc_global_tld}", path_begin = "/v2/providers/es-addon" } ] ``` -* Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [our Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/09-30-images-update.md b/content/changelog/2025/09-30-images-update.md index e51290568..c50f13169 100644 --- a/content/changelog/2025/09-30-images-update.md +++ b/content/changelog/2025/09-30-images-update.md @@ -14,33 +14,33 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.16.9 - * Clever Tools 4.1.0 - * Mise 2025.9.23 - * PostgreSQL Client 18.0 - * Varnish 8.0.0 - * Varnish modules 0.27.0 -* **Elixir:** - * Erlang 28.1 -* **FrankenPHP:** - * We now use glibc binary (1.9.1) -* **Java:** - * Gradle 9.1.0 -* **Node.js & Bun:** - * Update to 22.20.0 (npm 10.9.3) - * Bun 1.2.23 -* **PHP:** - * Update to PHP 8.3.26 - * Update to PHP 8.4.13 - * Excimer extension 1.2.5 (PHP 8.4 support) - * Composer 2.8.12 - * Symfony CLI 5.14.1 -* **Python:** - * uv 0.8.22 -* **Ruby:** - * Update to Ruby 3.4.6 -* **Static:** - * Hugo 0.150.1 -* **V:** - * Update to 0.4.12 +- **Common:** + - Linux kernel 6.16.9 + - Clever Tools 4.1.0 + - Mise 2025.9.23 + - PostgreSQL Client 18.0 + - Varnish 8.0.0 + - Varnish modules 0.27.0 +- **Elixir:** + - Erlang 28.1 +- **FrankenPHP:** + - We now use glibc binary (1.9.1) +- **Java:** + - Gradle 9.1.0 +- **Node.js & Bun:** + - Update to 22.20.0 (npm 10.9.3) + - Bun 1.2.23 +- **PHP:** + - Update to PHP 8.3.26 + - Update to PHP 8.4.13 + - Excimer extension 1.2.5 (PHP 8.4 support) + - Composer 2.8.12 + - Symfony CLI 5.14.1 +- **Python:** + - uv 0.8.22 +- **Ruby:** + - Update to Ruby 3.4.6 +- **Static:** + - Hugo 0.150.1 +- **V:** + - Update to 0.4.12 diff --git a/content/changelog/2025/10-07-images-update.md b/content/changelog/2025/10-07-images-update.md index 7c216ceee..158af6967 100644 --- a/content/changelog/2025/10-07-images-update.md +++ b/content/changelog/2025/10-07-images-update.md @@ -14,15 +14,15 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.16.10 - * Mise 2025.10.3 - * OpenSSL 3.5.4 - * Redis 8.2.2 - * Sops 3.11.0 -* **Java:** - * Update to Java 25 LTS -* **PHP:** - * Symfony CLI 5.15.1 -* **Python:** - * uv 0.8.23 +- **Common:** + - Linux kernel 6.16.10 + - Mise 2025.10.3 + - OpenSSL 3.5.4 + - Redis 8.2.2 + - Sops 3.11.0 +- **Java:** + - Update to Java 25 LTS +- **PHP:** + - Symfony CLI 5.15.1 +- **Python:** + - uv 0.8.23 diff --git a/content/changelog/2025/10-07-redis-8.2.2.md b/content/changelog/2025/10-07-redis-8.2.2.md index e6225106f..91d6eaffd 100644 --- a/content/changelog/2025/10-07-redis-8.2.2.md +++ b/content/changelog/2025/10-07-redis-8.2.2.md @@ -16,6 +16,7 @@ excludeSearch: true --- We updated Redis™ to [release 8.2.2](https://github.com/redis/redis/releases/tag/8.2.2). It brings bug fixes, enhancements, better performance and security patches for: + - [CVE-2025-46817](https://nvd.nist.gov/vuln/detail/CVE-2025-46817) - [CVE-2025-46818](https://nvd.nist.gov/vuln/detail/CVE-2025-46818) - [CVE-2025-46819](https://nvd.nist.gov/vuln/detail/CVE-2025-46819) diff --git a/content/changelog/2025/10-08-postgresql-18.md b/content/changelog/2025/10-08-postgresql-18.md index 01db43a1b..b477f8464 100644 --- a/content/changelog/2025/10-08-postgresql-18.md +++ b/content/changelog/2025/10-08-postgresql-18.md @@ -16,5 +16,5 @@ excludeSearch: true You can simply test it with a new add-on or update an existing one through automatic and safe Clever Cloud's migration process. -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) -* [PostgreSQL release notes](https://www.postgresql.org/docs/release/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [PostgreSQL release notes](https://www.postgresql.org/docs/release/) diff --git a/content/changelog/2025/10-16-otoroshi-17.6.md b/content/changelog/2025/10-16-otoroshi-17.6.md index 46cd35e86..ffdd426f6 100644 --- a/content/changelog/2025/10-16-otoroshi-17.6.md +++ b/content/changelog/2025/10-16-otoroshi-17.6.md @@ -29,4 +29,3 @@ clever otoroshi version update yourOtoroshiNameOrId v17.6.3_1760617667 - [Learn more about Biscuit Studio](https://cloud-apim.github.io/otoroshi-biscuit-studio/docs/overview) - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) - diff --git a/content/changelog/2025/10-21-clever-tools-4.2.md b/content/changelog/2025/10-21-clever-tools-4.2.md index 297cd82ee..00df3ab82 100644 --- a/content/changelog/2025/10-21-clever-tools-4.2.md +++ b/content/changelog/2025/10-21-clever-tools-4.2.md @@ -21,7 +21,7 @@ excludeSearch: true To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/10-22-clever-tools-4.3.md b/content/changelog/2025/10-22-clever-tools-4.3.md index 0b0e44fbe..8299facd6 100644 --- a/content/changelog/2025/10-22-clever-tools-4.3.md +++ b/content/changelog/2025/10-22-clever-tools-4.3.md @@ -21,7 +21,7 @@ excludeSearch: true To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/10-23-images-update.md b/content/changelog/2025/10-23-images-update.md index 6981b0787..657f86af8 100644 --- a/content/changelog/2025/10-23-images-update.md +++ b/content/changelog/2025/10-23-images-update.md @@ -14,41 +14,41 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.17.4 - * Clever Tools 4.3.0 - * Mise 2025.10.11 - * OpenSSH 10.2_p1 - * Ripgrep 15.0.0 - * Tailscale 1.88.3 -* **Docker:** - * Update to 28.5.1 - * Buildx 0.29.1 -* **Elixir:** - * Update to 1.19.1 - * Erlang 28.1.1 -* **Go:** - * Update to 1.25.3 -* **Node.js:** - * Update to 22.21.0 - * Bun 1.3.1 - * Yarn 4.10.3 -* **PHP:** - * Update to 8.3.27 - * Update to 8.4.14 - * memcached 1.6.39 - * memcached extension 3.4.0 -* **Python:** - * Update to Python 3.9.24 - * Update to Python 3.10.19 - * Update to Python 3.11.14 - * Update to Python 3.12.12 - * Update to Python 3.13.9 - * uv 0.9.4 -* **Ruby:** - * Update to Ruby 3.4.7 -* **Static:** - * Hugo 0.151.2 +- **Common:** + - Linux kernel 6.17.4 + - Clever Tools 4.3.0 + - Mise 2025.10.11 + - OpenSSH 10.2_p1 + - Ripgrep 15.0.0 + - Tailscale 1.88.3 +- **Docker:** + - Update to 28.5.1 + - Buildx 0.29.1 +- **Elixir:** + - Update to 1.19.1 + - Erlang 28.1.1 +- **Go:** + - Update to 1.25.3 +- **Node.js:** + - Update to 22.21.0 + - Bun 1.3.1 + - Yarn 4.10.3 +- **PHP:** + - Update to 8.3.27 + - Update to 8.4.14 + - memcached 1.6.39 + - memcached extension 3.4.0 +- **Python:** + - Update to Python 3.9.24 + - Update to Python 3.10.19 + - Update to Python 3.11.14 + - Update to Python 3.12.12 + - Update to Python 3.13.9 + - uv 0.9.4 +- **Ruby:** + - Update to Ruby 3.4.7 +- **Static:** + - Hugo 0.151.2 ## Node.js 25 support, 24 LTS as default version diff --git a/content/changelog/2025/10-27-terraform-1.3.0.md b/content/changelog/2025/10-27-terraform-1.3.0.md index e884861e2..9cc4a875d 100644 --- a/content/changelog/2025/10-27-terraform-1.3.0.md +++ b/content/changelog/2025/10-27-terraform-1.3.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.3.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.3.0) of the Clever Cloud Terraform provider is available. It brings multiple bug fixes, returns full URI of [MongoDB] add-ons as sensitive fields, and [MySQL] read-only users generated from the [Console](https://console.clever-cloud.com). It supports [.NET](/doc/applications/dotnet/) and [V (Vlang)](/doc/applications/v/) runtimes, [Configuration Providers](/doc/addons/config-provider/), and [Matomo](/doc/addons/matomo/) services. -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/10-31-otoroshi-17.7.md b/content/changelog/2025/10-31-otoroshi-17.7.md index c652ca29b..9fc00a8db 100644 --- a/content/changelog/2025/10-31-otoroshi-17.7.md +++ b/content/changelog/2025/10-31-otoroshi-17.7.md @@ -28,4 +28,3 @@ clever otoroshi version update yourOtoroshiNameOrId v17.7.0_1762500043 ``` - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) - diff --git a/content/changelog/2025/11-04-images-update.md b/content/changelog/2025/11-04-images-update.md index 36028c3bb..061c981c6 100644 --- a/content/changelog/2025/11-04-images-update.md +++ b/content/changelog/2025/11-04-images-update.md @@ -14,26 +14,26 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.16.12 (we had issues with previous 6.17.4) - * Mise 2025.10.18 - * Redis 8.2.3 - * Ripgrep 15.1.0 - * Tailscale 1.90.2 -* **Elixir:** - * Update to 1.19.2 - * Erlang 27.3.4.4 -* **Java:** - * Update to 11.0.29_p7 - * Update to 17.0.16_p8 - * Update to 21.0.9_p10 - * Update to 25.0.1_p8 - * Gradle 9.2.0 -* **Node.js:** - * Update to 24.11.0 LTS (npm 11.6.1) -* **Python:** - * pip 25.3 - * uv 0.9.7 -* **Static:** - * Hugo 0.152.1 - * Static Web Server 2.39.0 +- **Common:** + - Linux kernel 6.16.12 (we had issues with previous 6.17.4) + - Mise 2025.10.18 + - Redis 8.2.3 + - Ripgrep 15.1.0 + - Tailscale 1.90.2 +- **Elixir:** + - Update to 1.19.2 + - Erlang 27.3.4.4 +- **Java:** + - Update to 11.0.29_p7 + - Update to 17.0.16_p8 + - Update to 21.0.9_p10 + - Update to 25.0.1_p8 + - Gradle 9.2.0 +- **Node.js:** + - Update to 24.11.0 LTS (npm 11.6.1) +- **Python:** + - pip 25.3 + - uv 0.9.7 +- **Static:** + - Hugo 0.152.1 + - Static Web Server 2.39.0 diff --git a/content/changelog/2025/11-05-terraform-1.4.0.md b/content/changelog/2025/11-05-terraform-1.4.0.md index 6f0a9451a..da66ce79a 100644 --- a/content/changelog/2025/11-05-terraform-1.4.0.md +++ b/content/changelog/2025/11-05-terraform-1.4.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.4.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.4.0) of the Clever Cloud Terraform provider is available. It now uses API when available for Keycloak, Metabase, Otoroshi, rather than [service dependencies](/changelog/2025/10-22-operators-cleanup/). It brings support for [Elastic Stack](/doc/addons/elastic/) (Elasticsearch with APM, Kibana), load balancer datasource, and replaces applications when the region is changed. -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/11-12-images-update.md b/content/changelog/2025/11-12-images-update.md index d48d74058..a2eddd5db 100644 --- a/content/changelog/2025/11-12-images-update.md +++ b/content/changelog/2025/11-12-images-update.md @@ -14,31 +14,31 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.17.7 - * Clang, lld, lldb, LLVM 21.1.5 - * cURL 8.17.0 - * Mise 2025.11.1 - * Perl 5.42.0 - * Poppler 25.11.0 - * SQLite 3.51.0 - * Tailscale 1.90.6 -* **Go:** - * Update to 1.25.4 -* **PHP:** - * Apcu extension 5.1.27 - * Blackfire extension 1.92.49 - * Mailparse extension 3.1.9 - * Maxmind DB extension 1.12.1 - * MongoDB extension 1.21.2 - * OAuth extension 2.0.10 (PHP 7.1+, 2.0.9 remains for PHP 7.0) - * OpenTelemetry extension 1.2.1 - * Redis extension 6.2.0 - * Solr extension 2.8.1 - * Tideways extension 5.30.0 (Daemon 1.11.4) - * YAML extension 2.2.5 - * Zip extension 1.22.7 -* **Scala:** - * Play Framework 1.8 - * Play Framework 1.9 - * Play Framework 1.10 +- **Common:** + - Linux kernel 6.17.7 + - Clang, lld, lldb, LLVM 21.1.5 + - cURL 8.17.0 + - Mise 2025.11.1 + - Perl 5.42.0 + - Poppler 25.11.0 + - SQLite 3.51.0 + - Tailscale 1.90.6 +- **Go:** + - Update to 1.25.4 +- **PHP:** + - Apcu extension 5.1.27 + - Blackfire extension 1.92.49 + - Mailparse extension 3.1.9 + - Maxmind DB extension 1.12.1 + - MongoDB extension 1.21.2 + - OAuth extension 2.0.10 (PHP 7.1+, 2.0.9 remains for PHP 7.0) + - OpenTelemetry extension 1.2.1 + - Redis extension 6.2.0 + - Solr extension 2.8.1 + - Tideways extension 5.30.0 (Daemon 1.11.4) + - YAML extension 2.2.5 + - Zip extension 1.22.7 +- **Scala:** + - Play Framework 1.8 + - Play Framework 1.9 + - Play Framework 1.10 diff --git a/content/changelog/2025/11-12-keycloak-26.4.4.md b/content/changelog/2025/11-12-keycloak-26.4.4.md index 24ef7c655..3024f7f82 100644 --- a/content/changelog/2025/11-12-keycloak-26.4.4.md +++ b/content/changelog/2025/11-12-keycloak-26.4.4.md @@ -16,6 +16,7 @@ excludeSearch: true --- [The release 26.4.4](https://github.com/keycloak/keycloak/releases/26.4.4) of Keycloak is available on Clever Cloud. It brings multiple enhancements and bug fixes. Starting with this release, Keycloak is deployed with two new plugins: + - [Groups regexp mapper](https://github.com/please-openit/keycloak-groups-regexp-mapper): map groups that only fit to a regexp (by name), it avoids mapping all user groups into a token - [HTTP Get request mapper](https://github.com/please-openit/keycloak-http-get-request-mapper): add a result from an external service (HTTP GET request) to user's tokens diff --git a/content/changelog/2025/11-13-clever-tools-4.4.md b/content/changelog/2025/11-13-clever-tools-4.4.md index b1d73ea91..c456b84c6 100644 --- a/content/changelog/2025/11-13-clever-tools-4.4.md +++ b/content/changelog/2025/11-13-clever-tools-4.4.md @@ -30,7 +30,7 @@ otoroshictl resources get routes To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2025/11-13-otoroshi-plugins.md b/content/changelog/2025/11-13-otoroshi-plugins.md index d39ec9193..b015913d4 100644 --- a/content/changelog/2025/11-13-otoroshi-plugins.md +++ b/content/changelog/2025/11-13-otoroshi-plugins.md @@ -16,8 +16,10 @@ excludeSearch: true --- Otoroshi on Clever Cloud comes with many included plugins to manage AI based APIs, Model Context Protocol (MCP), API Portal, Biscuit authentication, Dynamic JS or Mailer, etc. Now you can also load any other custom plugin by setting the `CC_OTOROSHI_PLUGINS_URLS` environment variable of the underlying Java application: + - Add URLs pointing to the plugins JAR files, separated by a space or a new line - Rebuild the application This works for any Otoroshi version deployed on Clever Cloud. If you want to learn more about Otoroshi plugins and how to create your own, check the [Otoroshi documentation](https://maif.github.io/otoroshi/manual/docs/plugins/create-plugins). + - [Learn more about Otoroshi with LLM on Clever Cloud](/doc/addons/otoroshi/) diff --git a/content/changelog/2025/11-17-terraform-1.5.0.md b/content/changelog/2025/11-17-terraform-1.5.0.md index 13920f522..e5399e095 100644 --- a/content/changelog/2025/11-17-terraform-1.5.0.md +++ b/content/changelog/2025/11-17-terraform-1.5.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.5.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.5.0) of the Clever Cloud Terraform provider is available. It brings bug fixes, support for [Network Groups](/doc/develop/network-groups/), GitHub applications from public or private repositories, migration of PostgreSQL databases. -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/11-19-terraform-1.6.0.md b/content/changelog/2025/11-19-terraform-1.6.0.md index 2ea17a346..177ad608a 100644 --- a/content/changelog/2025/11-19-terraform-1.6.0.md +++ b/content/changelog/2025/11-19-terraform-1.6.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.6.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.6.0) of the Clever Cloud Terraform provider is available. It brings bug fixes and allows to disable [Network Groups](/doc/develop/network-groups/). -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/12-01-metabase-57-default.md b/content/changelog/2025/12-01-metabase-57-default.md index 3526c83a6..819f95e00 100644 --- a/content/changelog/2025/12-01-metabase-57-default.md +++ b/content/changelog/2025/12-01-metabase-57-default.md @@ -16,6 +16,7 @@ excludeSearch: true --- The `x.57` branch of Metabase is available on Clever Cloud [since last month](/changelog/2025/11-10-metabase-57/). It's now the default branch deployed with the [release 0.57.4](https://github.com/metabase/metabase/releases/tag/v0.57.4). It means that: + - All new add-ons will use it - All add-ons using default configuration (`community-latest`) will use it after a rebuild diff --git a/content/changelog/2025/12-01-terraform-1.7.0.md b/content/changelog/2025/12-01-terraform-1.7.0.md index 3ae3443ac..d4ad436d0 100644 --- a/content/changelog/2025/12-01-terraform-1.7.0.md +++ b/content/changelog/2025/12-01-terraform-1.7.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.7.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.7.0) of the Clever Cloud Terraform provider is available. It brings bug fixes, reboot action for applications and encryption at rest for PostgreSQL databases. -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2025/12-03-otoroshi-17.9.md b/content/changelog/2025/12-03-otoroshi-17.9.md index 7c4164aad..a7656a183 100644 --- a/content/changelog/2025/12-03-otoroshi-17.9.md +++ b/content/changelog/2025/12-03-otoroshi-17.9.md @@ -16,6 +16,7 @@ excludeSearch: true --- [Otoroshi v17.9](https://github.com/MAIF/otoroshi/releases/tag/v17.9.0) is available with multiple improvements and bug fixes and two new plugins we've contributed to: + - LLMs.txt Accept Markdown: proxies requests with `Accept: text/markdown` header according to [llms.txt proposal](https://llmstxt.org/) - Swagger UI: serves a Swagger UI page from a configurable OpenAPI specification URL diff --git a/content/changelog/2025/12-15-pulsar-ga.md b/content/changelog/2025/12-15-pulsar-ga.md index 505716908..1a27fa9e1 100644 --- a/content/changelog/2025/12-15-pulsar-ga.md +++ b/content/changelog/2025/12-15-pulsar-ga.md @@ -21,25 +21,25 @@ Apache Pulsar is out of Beta on Clever Cloud. It doesn't change anything for exi Monthly prices will be as follows: -| Traffic IN/OUT | Price per GB | -|--------|--------------| -| < 512 MB | Free | -| 512 MB - 100 GB | €0.800 | -| 100 GB - 500 GB | €0.500 | -| 500 GB - 5 TB | €0.400 | -| > 5 TB | €0.300 | +| Traffic IN/OUT | Price per GB | +| --------------- | ------------ | +| < 512 MB | Free | +| 512 MB - 100 GB | €0.800 | +| 100 GB - 500 GB | €0.500 | +| 500 GB - 5 TB | €0.400 | +| > 5 TB | €0.300 | | Hot Storage (SSD) | Price per GB | -|--------|--------------| -| < 256 MB | Free | -| 256 MB - 50 GB | €0.200 | -| 50 GB - 250 GB | €0.150 | -| 250 GB - 1 TB | €0.120 | -| > 1 TB | €0.100 | +| ----------------- | ------------ | +| < 256 MB | Free | +| 256 MB - 50 GB | €0.200 | +| 50 GB - 250 GB | €0.150 | +| 250 GB - 1 TB | €0.120 | +| > 1 TB | €0.100 | | Cold Storage (HDD) | Price per GB | -|------|--------------| -| All volumes | €0.015 | +| ------------------ | ------------ | +| All volumes | €0.015 | In the coming weeks, we will update the Pulsar dashboard and product page to reflect these changes. diff --git a/content/changelog/2025/12-17-images-update.md b/content/changelog/2025/12-17-images-update.md index 199fc4ce3..9b864d1ec 100644 --- a/content/changelog/2025/12-17-images-update.md +++ b/content/changelog/2025/12-17-images-update.md @@ -14,43 +14,43 @@ excludeSearch: true We updated all our images except FrankenPHP and PHP. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.17.12 - * Anubis 1.23.1 - * Clever Tools 4.4.1 - * Git 2.52.0 - * Mise 2025.12.1 - * Nano 8.7 - * Poppler 25.12.0 - * Postgresql-client 18.1 - * Redis 8.4 - * Rust 1.92.0 - * Tailscale 1.90.9 - * Tmux 3.6 -* **Docker:** - * Docker 29.0.2 - * Docker Buildx 0.30.1 -* **Elixir:** - * Update to 1.19.4 - * Erlang 26.2.5.16 - * Erlang 27.3.4.6 - * Erlang 28.2 -* **Go:** - * Update to 1.25.5 -* **Node.js:** - * Update to 24.11.1 (npm 11.6.1) - * Bun 1.3.4 - * Yarn 4.12.0 -* **Python:** - * Update to 3.9.25 - * Update to 3.12.0 - * Update to 3.13.11 - * uv 0.9.17 -* **Ruby:** - * Update to 3.3.10 - * Update to 3.4.7 -* **Static:** - * Static Web Server 2.40.1 +- **Common:** + - Linux kernel 6.17.12 + - Anubis 1.23.1 + - Clever Tools 4.4.1 + - Git 2.52.0 + - Mise 2025.12.1 + - Nano 8.7 + - Poppler 25.12.0 + - Postgresql-client 18.1 + - Redis 8.4 + - Rust 1.92.0 + - Tailscale 1.90.9 + - Tmux 3.6 +- **Docker:** + - Docker 29.0.2 + - Docker Buildx 0.30.1 +- **Elixir:** + - Update to 1.19.4 + - Erlang 26.2.5.16 + - Erlang 27.3.4.6 + - Erlang 28.2 +- **Go:** + - Update to 1.25.5 +- **Node.js:** + - Update to 24.11.1 (npm 11.6.1) + - Bun 1.3.4 + - Yarn 4.12.0 +- **Python:** + - Update to 3.9.25 + - Update to 3.12.0 + - Update to 3.13.11 + - uv 0.9.17 +- **Ruby:** + - Update to 3.3.10 + - Update to 3.4.7 +- **Static:** + - Static Web Server 2.40.1 ## Elixir support diff --git a/content/changelog/2025/12-19-terraform-1.8.0.md b/content/changelog/2025/12-19-terraform-1.8.0.md index a77805f65..cada13af0 100644 --- a/content/changelog/2025/12-19-terraform-1.8.0.md +++ b/content/changelog/2025/12-19-terraform-1.8.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.8.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.8.0) of the Clever Cloud Terraform provider is available. It brings bug fixes, `clevercloud_database_query` action, upload action for FS Buckets, exposed environment variables for applications and more Clever Cloud's Keycloak features. -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2026/01-14-clever-tools-4.5.md b/content/changelog/2026/01-14-clever-tools-4.5.md index fe29f13ab..0acbacec9 100644 --- a/content/changelog/2026/01-14-clever-tools-4.5.md +++ b/content/changelog/2026/01-14-clever-tools-4.5.md @@ -35,7 +35,7 @@ These changes were an essential building block for the next major step: redesign To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2026/01-15-images-update.md b/content/changelog/2026/01-15-images-update.md index 24b1131b5..30bf8f5b9 100644 --- a/content/changelog/2026/01-15-images-update.md +++ b/content/changelog/2026/01-15-images-update.md @@ -14,46 +14,46 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.17.13 - * cURL 8.18.0 - * NGINX 1.28.1 - * OAuth2 Proxy 7.13.0 - * Otoroshictl 0.0.13 - * Poppler 26.01.0 - * Tailscale 1.92.4 -* **Docker:** - * Update to 29.1.4 -* **Elixir:** - * Update to 1.19.5 - * Erlang 28.3 -* **Node.js:** - * Update to 24.13.0 (npm 11.6.2) - * Bun 1.3.6 -* **PHP:** - * Update to 8.1.34 - * Update to 8.2.30 - * Update to 8.3.29 - * Update to 8.4.16 - * Update to 8.5.1 - * Composer 2.9.2 - * Symfony CLI 5.16.1 - * Blackfire extension 1.92.51 - * gRPC extension 1.76.0 - * Imagick extension 3.8.1 - * Maxmind DB extension 1.13.0 - * New Relic extension 12.2.0.27 - * Protobuf extension 3.23.4, 3.25.8 and 4.33.2 - * Redis extension 6.3.0 - * Tideways extension 5.31.0 (Daemon 1.11.4) - * YAML extension 2.3.0 -* **Python:** - * uv 0.9.24 -* **Ruby:** - * Update to 3.4.8 - * Update to 4.0.1 -* **V (Vlang):** - * Update to 0.5 +- **Common:** + - Linux kernel 6.17.13 + - cURL 8.18.0 + - NGINX 1.28.1 + - OAuth2 Proxy 7.13.0 + - Otoroshictl 0.0.13 + - Poppler 26.01.0 + - Tailscale 1.92.4 +- **Docker:** + - Update to 29.1.4 +- **Elixir:** + - Update to 1.19.5 + - Erlang 28.3 +- **Node.js:** + - Update to 24.13.0 (npm 11.6.2) + - Bun 1.3.6 +- **PHP:** + - Update to 8.1.34 + - Update to 8.2.30 + - Update to 8.3.29 + - Update to 8.4.16 + - Update to 8.5.1 + - Composer 2.9.2 + - Symfony CLI 5.16.1 + - Blackfire extension 1.92.51 + - gRPC extension 1.76.0 + - Imagick extension 3.8.1 + - Maxmind DB extension 1.13.0 + - New Relic extension 12.2.0.27 + - Protobuf extension 3.23.4, 3.25.8 and 4.33.2 + - Redis extension 6.3.0 + - Tideways extension 5.31.0 (Daemon 1.11.4) + - YAML extension 2.3.0 +- **Python:** + - uv 0.9.24 +- **Ruby:** + - Update to 3.4.8 + - Update to 4.0.1 +- **V (Vlang):** + - Update to 0.5 ## PHP 8.5 and Composer 2.9 diff --git a/content/changelog/2026/01-23-terraform-1.9.0.md b/content/changelog/2026/01-23-terraform-1.9.0.md index 84d820929..4e90befb3 100644 --- a/content/changelog/2026/01-23-terraform-1.9.0.md +++ b/content/changelog/2026/01-23-terraform-1.9.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.9.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.9.0) of the Clever Cloud Terraform provider is available. It brings bug fixes, Java JAR support, app-to-app dependencies, more integrations and backup datasource for PostgreSQL. -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2026/01-27-metrics-retention-change.md b/content/changelog/2026/01-27-metrics-retention-change.md index 4d834da39..18341f16e 100644 --- a/content/changelog/2026/01-27-metrics-retention-change.md +++ b/content/changelog/2026/01-27-metrics-retention-change.md @@ -16,5 +16,5 @@ Starting February 2nd, 2026, the retention period for custom application metrics **System metrics remain unaffected** by this change. All automatically collected metrics, including CPU usage, RAM consumption, disk usage, network activity, and load metrics, will continue to be available with the existing retention period. -* [Learn more about metrics on Clever Cloud](/doc/metrics/) -* [How to publish your own metrics](/doc/metrics/#publish-your-own-metrics) +- [Learn more about metrics on Clever Cloud](/doc/metrics/) +- [How to publish your own metrics](/doc/metrics/#publish-your-own-metrics) diff --git a/content/changelog/2026/01-28-images-update.md b/content/changelog/2026/01-28-images-update.md index 41ae82cc3..f632d1602 100644 --- a/content/changelog/2026/01-28-images-update.md +++ b/content/changelog/2026/01-28-images-update.md @@ -14,38 +14,39 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Clever Tools 4.5.3 - * qpdf 12.3.2 - * Mise 2026.1.8 -* **Docker:** - * Update to 29.2.0 - * Docker Buildx 0.31.0 -* **Elixir:** - * Erlang 28.3.1 -* **Go:** - * Update to 1.25.6 -* **Java:** - * Gradle 8.14.4 - * Gradle 9.3.0 - * Maven 3.9.12 -* **Node.js & Bun:** - * Bun 1.3.7 -* **PHP:** - * Update to 8.3.30 - * Update to 8.4.17 - * Update to 8.5.2 - * Composer 2.9.4 -* **Python:** - * uv 0.9.26 -* **Ruby:** - * Update to 3.2.10 -* **Rust:** - * Update to 1.93.0 +- **Common:** + - Clever Tools 4.5.3 + - qpdf 12.3.2 + - Mise 2026.1.8 +- **Docker:** + - Update to 29.2.0 + - Docker Buildx 0.31.0 +- **Elixir:** + - Erlang 28.3.1 +- **Go:** + - Update to 1.25.6 +- **Java:** + - Gradle 8.14.4 + - Gradle 9.3.0 + - Maven 3.9.12 +- **Node.js & Bun:** + - Bun 1.3.7 +- **PHP:** + - Update to 8.3.30 + - Update to 8.4.17 + - Update to 8.5.2 + - Composer 2.9.4 +- **Python:** + - uv 0.9.26 +- **Ruby:** + - Update to 3.2.10 +- **Rust:** + - Update to 1.93.0 ## Request Flow expansion Request Flow is now available in .NET, Elixir, Haskell and Rust applications. We plan its expansion to more runtimes in the coming weeks in three releases: + - Go and Node.js/Bun - Java and PHP - Python and Ruby diff --git a/content/changelog/2026/02-03-images-update.md b/content/changelog/2026/02-03-images-update.md index c20166cad..afb9c61c7 100644 --- a/content/changelog/2026/02-03-images-update.md +++ b/content/changelog/2026/02-03-images-update.md @@ -14,30 +14,30 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * OAuth2 Proxy 7.14.2 - * Otoroshictl 0.0.15 - * Tailscale 1.94.1 -* **.NET:** - * Update to 6.0.136 -* **Docker:** - * Docker Buildx 0.31.1 -* **Java:** - * Update to 11.0.30_p7 - * Update to 17.0.18_p8 - * Update to 21.0.10_p7 - * Update to 25.0.2_p10 - * Gradle 9.3.1 -* **Node.js & Bun:** - * Bun 1.3.8 -* **PHP:** - * Composer 2.9.5 - * mcrypt extension 1.0.9 - * PDFlib extension 11.0.0 - * solr extension 2.9.1 - * xdebug extension 3.5.0 -* **Python:** - * uv 0.9.28 +- **Common:** + - OAuth2 Proxy 7.14.2 + - Otoroshictl 0.0.15 + - Tailscale 1.94.1 +- **.NET:** + - Update to 6.0.136 +- **Docker:** + - Docker Buildx 0.31.1 +- **Java:** + - Update to 11.0.30_p7 + - Update to 17.0.18_p8 + - Update to 21.0.10_p7 + - Update to 25.0.2_p10 + - Gradle 9.3.1 +- **Node.js & Bun:** + - Bun 1.3.8 +- **PHP:** + - Composer 2.9.5 + - mcrypt extension 1.0.9 + - PDFlib extension 11.0.0 + - solr extension 2.9.1 + - xdebug extension 3.5.0 +- **Python:** + - uv 0.9.28 ## Docker Buildx diff --git a/content/changelog/2026/02-12-images-update.md b/content/changelog/2026/02-12-images-update.md index cfd658ae4..32de9f0f0 100644 --- a/content/changelog/2026/02-12-images-update.md +++ b/content/changelog/2026/02-12-images-update.md @@ -14,25 +14,25 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * git 2.53.0 - * Mise 2026.2.8 - * nginx 1.28.2 - * pgpool2 4.7 -* **.NET:** - * Update to 10.0.102 -* **Docker:** - * Docker 29.2.1 -* **Go:** - * Update to 1.26.0 -* **Node.js & Bun:** - * Bun 1.3.9 - * nvm 0.40.4 -* **Python:** - * Update to 3.13.12 - * Update to 3.14.3 - * pip 26.0.1 - * uv 0.10.2 +- **Common:** + - git 2.53.0 + - Mise 2026.2.8 + - nginx 1.28.2 + - pgpool2 4.7 +- **.NET:** + - Update to 10.0.102 +- **Docker:** + - Docker 29.2.1 +- **Go:** + - Update to 1.26.0 +- **Node.js & Bun:** + - Bun 1.3.9 + - nvm 0.40.4 +- **Python:** + - Update to 3.13.12 + - Update to 3.14.3 + - pip 26.0.1 + - uv 0.10.2 ## .NET 10 support @@ -51,5 +51,6 @@ Latest branch of Mise includes many changes such as Node.js version detection fr ## Fixes This release includes: + - A fix for [Mise Tasks management in Linux Runtime](/doc/applications/linux/#build-and-run-commands) - A fix for `CC_NODE_BUILD_TOOL=yarn-berry` diff --git a/content/changelog/2026/02-18-clever-tools-4.6.md b/content/changelog/2026/02-18-clever-tools-4.6.md index fb335674f..cd7e61bc5 100644 --- a/content/changelog/2026/02-18-clever-tools-4.6.md +++ b/content/changelog/2026/02-18-clever-tools-4.6.md @@ -99,7 +99,7 @@ clever features disable system-git To upgrade Clever Tools, [use your favorite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2026/02-18-images-update.md b/content/changelog/2026/02-18-images-update.md index e8c0926dc..eebed4784 100644 --- a/content/changelog/2026/02-18-images-update.md +++ b/content/changelog/2026/02-18-images-update.md @@ -14,23 +14,23 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Linux Kernel 6.19.2 - * ClamAV 1.5.1 - * Mise 2026.2.13 - * Otoroshictl 0.0.16 -* **.NET:** - * Update to 8.0.123 - * Update to 9.0.113 -* **Elixir:** - * Erlang 27.3.4.7 -* **Node.js & Bun:** - * Update to 24.13.1 (npm 11.8.0) -* **PHP:** - * Update to 8.4.18 - * Update to 8.5.3 -* **Rust:** - * Update to 1.93.1 +- **Common:** + - Linux Kernel 6.19.2 + - ClamAV 1.5.1 + - Mise 2026.2.13 + - Otoroshictl 0.0.16 +- **.NET:** + - Update to 8.0.123 + - Update to 9.0.113 +- **Elixir:** + - Erlang 27.3.4.7 +- **Node.js & Bun:** + - Update to 24.13.1 (npm 11.8.0) +- **PHP:** + - Update to 8.4.18 + - Update to 8.5.3 +- **Rust:** + - Update to 1.93.1 ## Apache Basic Auth diff --git a/content/changelog/2026/02-25-images-update.md b/content/changelog/2026/02-25-images-update.md index 5659d6cad..c8af66b7a 100644 --- a/content/changelog/2026/02-25-images-update.md +++ b/content/changelog/2026/02-25-images-update.md @@ -14,19 +14,19 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Clever Tools 4.6.1 - * md4c 0.5.2 - * Redis 8.6.1 - * Mise 2026.2.19 - * Otoroshictl 0.0.17 -* **FrankenPHP:** - * Update to 1.11.3 (with `CC_PHP_VERSION=8.5`) -* **Python:** - * uv 0.10.4 -* **Static:** - * Caddy 2.11.1 - * Static Web Server 2.41.0 +- **Common:** + - Clever Tools 4.6.1 + - md4c 0.5.2 + - Redis 8.6.1 + - Mise 2026.2.19 + - Otoroshictl 0.0.17 +- **FrankenPHP:** + - Update to 1.11.3 (with `CC_PHP_VERSION=8.5`) +- **Python:** + - uv 0.10.4 +- **Static:** + - Caddy 2.11.1 + - Static Web Server 2.41.0 ## PHP Version in FrankenPHP diff --git a/content/changelog/2026/03-03-terraform-1.10.0.md b/content/changelog/2026/03-03-terraform-1.10.0.md index 6a8000a9f..722aea4bb 100644 --- a/content/changelog/2026/03-03-terraform-1.10.0.md +++ b/content/changelog/2026/03-03-terraform-1.10.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.10.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.10.0) of the Clever Cloud Terraform provider is available. It adds Linux runtime support for applications, missing feature flags for MySQL, PostgreSQL and MongoDB add-ons, and includes multiple bug fixes (add-on API responses, app state handling, Elasticsearch versioning, OAuth signature method). -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2026/03-09-pg-update-18.3.md b/content/changelog/2026/03-09-pg-update-18.3.md index 358790e41..f83010cec 100644 --- a/content/changelog/2026/03-09-pg-update-18.3.md +++ b/content/changelog/2026/03-09-pg-update-18.3.md @@ -13,12 +13,13 @@ excludeSearch: true --- New PostgreSQL versions are available for new add-ons and migration: -* PostgreSQL 18.3 -* PostgreSQL 17.9 -* PostgreSQL 16.13 -* PostgreSQL 15.17 -* PostgreSQL 14.22 + +- PostgreSQL 18.3 +- PostgreSQL 17.9 +- PostgreSQL 16.13 +- PostgreSQL 15.17 +- PostgreSQL 14.22 These versions include [multiple bug fixes and improvements](https://www.postgresql.org/about/news/postgresql-183-179-1613-1517-and-1422-released-3246/). PostgreSQL 14 and 15 are now shipped without `plls` and `plcoffee` extensions, which are no longer supported with up to date add-ons. -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2026/03-11-clever-tools-4.7.md b/content/changelog/2026/03-11-clever-tools-4.7.md index 4294dcee2..587bec463 100644 --- a/content/changelog/2026/03-11-clever-tools-4.7.md +++ b/content/changelog/2026/03-11-clever-tools-4.7.md @@ -58,7 +58,7 @@ The `clever drain` and `clever drain get` commands now display message rates and To upgrade Clever Tools, [use your favourite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2026/03-19-drains-legacy.md b/content/changelog/2026/03-19-drains-legacy.md index 31a9aa940..c39eb8c70 100644 --- a/content/changelog/2026/03-19-drains-legacy.md +++ b/content/changelog/2026/03-19-drains-legacy.md @@ -15,6 +15,7 @@ excludeSearch: true For the past few months, Clever Cloud has provided a new drain stack and a `v4` API that is more reliable and provides better monitoring. It's available through Clever Tools since [release `4.4.0`](https://github.com/CleverCloud/clever-tools/releases/tag/4.4.0). If you still use the legacy drains implementation, we encourage you to migrate to the new stack as soon as possible. The legacy drains will be disabled on June 1st, 2026. In the meantime, we will make some changes to prepare for the migration and help you transition smoothly: + - March 19th, 2026: we added `CC_PREVENT_LEGACY_LOGSCOLLECTION=false` environment variable on applications linked to a legacy active drain - Starting with the next image release, if not set to `false`, we will consider this environment variable as `true` and only push logs of applications with a drain to the new drain stack - June 1st, 2026: legacy drains will be disabled and all applications will need to use the new drain stack to receive logs @@ -25,4 +26,4 @@ To migrate to the new drain stack, just create a new drain with Clever Tools, th If you have any questions or if you need help with the migration, contact [our support team](https://console.clever-cloud.com/ticket-center-choice). -- [Learn more about Clever Tools drain command ](/doc/cli/logs-drains/) +- [Learn more about Clever Tools drain command](/doc/cli/logs-drains/) diff --git a/content/changelog/2026/03-23-metabase-59-default.md b/content/changelog/2026/03-23-metabase-59-default.md index 506bf1850..4efe3292d 100644 --- a/content/changelog/2026/03-23-metabase-59-default.md +++ b/content/changelog/2026/03-23-metabase-59-default.md @@ -16,6 +16,7 @@ excludeSearch: true --- The `x.59` branch of Metabase is available on Clever Cloud [since earlier this month](/changelog/2026/03-03-metabase-59/). It's now the default branch deployed with the [release 0.59.3](https://github.com/metabase/metabase/releases/tag/v0.59.3). It means that: + - All new add-ons will use it - All add-ons using default configuration (`community-latest`) will use it after a rebuild diff --git a/content/changelog/2026/03-25-images-update.md b/content/changelog/2026/03-25-images-update.md index 353d5795d..2b8710ed4 100644 --- a/content/changelog/2026/03-25-images-update.md +++ b/content/changelog/2026/03-25-images-update.md @@ -14,46 +14,46 @@ excludeSearch: true We updated all our images, except PHP. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.19.7 - * Anubis 1.25.0 - * Chromium 146.0.7680.153 - * Clever Tools 4.7.1 - * cURL 8.19.0 - * FFmpeg 8.1 - * Ghostscript 10.07 - * OAuth2 Proxy 7.14.3 - * Poppler 26.03 - * SQLite 3.52.0 - * Varnish 8.0.1 - * Vim 9.2.0096 -* **Elixir:** - * Erlang 26.2.5.18 - * Erlang 27.3.4.9 - * Erlang 28.4.1 -* **FrankenPHP:** - * Update to 1.12.1 with PHP 8.5.4 (with `CC_PHP_VERSION=8.5`) -* **Go:** - * Update to 1.26.1 -* **Java:** - * Gradle 9.4.1 - * Maven 3.9.14 -* **Node.js & Bun:** - * Update to 24.14.1 (npm 11.11.0) - * Bun 1.3.11 - * Yarn 4.13.0 -* **Python:** - * Update to 3.10.20 - * Update to 3.11.15 - * Update to 3.12.13 - * uv 0.10.12 -* **Ruby:** - * Update to 3.4.9 -* **Rust:** - * Update to 1.94.0 - * Rustup 1.29.0 -* **Static:** - * Caddy 2.11.2 +- **Common:** + - Linux kernel 6.19.7 + - Anubis 1.25.0 + - Chromium 146.0.7680.153 + - Clever Tools 4.7.1 + - cURL 8.19.0 + - FFmpeg 8.1 + - Ghostscript 10.07 + - OAuth2 Proxy 7.14.3 + - Poppler 26.03 + - SQLite 3.52.0 + - Varnish 8.0.1 + - Vim 9.2.0096 +- **Elixir:** + - Erlang 26.2.5.18 + - Erlang 27.3.4.9 + - Erlang 28.4.1 +- **FrankenPHP:** + - Update to 1.12.1 with PHP 8.5.4 (with `CC_PHP_VERSION=8.5`) +- **Go:** + - Update to 1.26.1 +- **Java:** + - Gradle 9.4.1 + - Maven 3.9.14 +- **Node.js & Bun:** + - Update to 24.14.1 (npm 11.11.0) + - Bun 1.3.11 + - Yarn 4.13.0 +- **Python:** + - Update to 3.10.20 + - Update to 3.11.15 + - Update to 3.12.13 + - uv 0.10.12 +- **Ruby:** + - Update to 3.4.9 +- **Rust:** + - Update to 1.94.0 + - Rustup 1.29.0 +- **Static:** + - Caddy 2.11.2 ## PHP & Composer version diff --git a/content/changelog/2026/03-31-mysql-8.0.45-8.4.8.md b/content/changelog/2026/03-31-mysql-8.0.45-8.4.8.md index 625ce0561..efa73e73f 100644 --- a/content/changelog/2026/03-31-mysql-8.0.45-8.4.8.md +++ b/content/changelog/2026/03-31-mysql-8.0.45-8.4.8.md @@ -14,6 +14,6 @@ excludeSearch: true We updated MySQL images for releases 8.4 (8.4.8-8) and 8.0 (8.0.45-36). They're now available for add-ons creation and migration. -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) -* [Learn more about MySQL 8.0.45](https://docs.percona.com/percona-server/8.0/release-notes/8.0.45-36.html) -* [Learn more about MySQL 8.4.8](https://docs.percona.com/percona-server/8.4/release-notes/8.4.8-8.html) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.0.45](https://docs.percona.com/percona-server/8.0/release-notes/8.0.45-36.html) +- [Learn more about MySQL 8.4.8](https://docs.percona.com/percona-server/8.4/release-notes/8.4.8-8.html) diff --git a/content/changelog/2026/04-02-terraform-1.11.0.md b/content/changelog/2026/04-02-terraform-1.11.0.md index 255a45f82..7a28dc663 100644 --- a/content/changelog/2026/04-02-terraform-1.11.0.md +++ b/content/changelog/2026/04-02-terraform-1.11.0.md @@ -17,4 +17,4 @@ excludeSearch: true The [1.11.0 release](https://github.com/CleverCloud/terraform-provider-clevercloud/releases/tag/v1.11.0) of the Clever Cloud Terraform provider is available. It adds new resources (add-on provider, OAuth consumer, vulnerability scanner), improved validation for environment variables, add-on options and plan slugs, exponential backoff retry for 503 errors, and multiple bug fixes. -* Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) +- Learn more about [Clever Cloud Terraform provider](https://registry.terraform.io/providers/CleverCloud/clevercloud/latest/docs) diff --git a/content/changelog/2026/04-07-images-update.md b/content/changelog/2026/04-07-images-update.md index 16827c560..c61dcdf1b 100644 --- a/content/changelog/2026/04-07-images-update.md +++ b/content/changelog/2026/04-07-images-update.md @@ -14,21 +14,21 @@ excludeSearch: true We updated all our images, except PHP. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.19.11 - * nginx 1.28.3 - * OAuth2 Proxy 7.15.1 - * Tailscale 1.96.3 -* **Python:** - * uv 0.11.2 -* **Ruby:** - * Update to 3.3.11 - * Update to 4.0.2 -* **Static:** - * mdBook 0.5.2 - * Static Web Server 2.42.0 -* **V (Vlang):** - * Update to 0.5.1 +- **Common:** + - Linux kernel 6.19.11 + - nginx 1.28.3 + - OAuth2 Proxy 7.15.1 + - Tailscale 1.96.3 +- **Python:** + - uv 0.11.2 +- **Ruby:** + - Update to 3.3.11 + - Update to 4.0.2 +- **Static:** + - mdBook 0.5.2 + - Static Web Server 2.42.0 +- **V (Vlang):** + - Update to 0.5.1 ## mdBook 0.5 diff --git a/content/changelog/2026/04-09-clever-tools-4.8.md b/content/changelog/2026/04-09-clever-tools-4.8.md index b203d8bb3..e29d9a55a 100644 --- a/content/changelog/2026/04-09-clever-tools-4.8.md +++ b/content/changelog/2026/04-09-clever-tools-4.8.md @@ -75,7 +75,7 @@ clever drain remove --addon postgresql_xxxxxxxx To upgrade Clever Tools, [use your favourite package manager](/doc/cli/install/). For example with `npm`: -``` +```console npm update -g clever-tools clever version ``` diff --git a/content/changelog/2026/04-21-images-update.md b/content/changelog/2026/04-21-images-update.md index 972f24d7a..f3654efa0 100644 --- a/content/changelog/2026/04-21-images-update.md +++ b/content/changelog/2026/04-21-images-update.md @@ -14,27 +14,27 @@ excludeSearch: true We updated all our images, except PHP. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.19.13 - * Chromium 147.0.7727.101 - * Clever Tools 4.8.0 - * htop 3.5.0 - * nano 9.0 - * nginx 1.30.0 - * OpenSSH 10.3p1 - * OpenSSL 3.5.6 - * SQLite 3.53.0 -* **FrankenPHP:** - * Update to 1.12.2 (with `CC_PHP_VERSION=8.5`) - * Symfony CLI 5.17.1 -* **Go:** - * Update to 1.26.2 -* **Node.js & Bun:** - * Update to 24.15.0 (npm 11.12.1) - * Bun 1.3.12 -* **Python:** - * Update to 3.13.13 - * Update to 3.14.4 - * uv 0.11.7 -* **Ruby:** - * Update to 3.2.11 +- **Common:** + - Linux kernel 6.19.13 + - Chromium 147.0.7727.101 + - Clever Tools 4.8.0 + - htop 3.5.0 + - nano 9.0 + - nginx 1.30.0 + - OpenSSH 10.3p1 + - OpenSSL 3.5.6 + - SQLite 3.53.0 +- **FrankenPHP:** + - Update to 1.12.2 (with `CC_PHP_VERSION=8.5`) + - Symfony CLI 5.17.1 +- **Go:** + - Update to 1.26.2 +- **Node.js & Bun:** + - Update to 24.15.0 (npm 11.12.1) + - Bun 1.3.12 +- **Python:** + - Update to 3.13.13 + - Update to 3.14.4 + - uv 0.11.7 +- **Ruby:** + - Update to 3.2.11 diff --git a/content/changelog/2026/04-23-otoroshi-17.15.md b/content/changelog/2026/04-23-otoroshi-17.15.md index c902e3555..8f7939e0a 100644 --- a/content/changelog/2026/04-23-otoroshi-17.15.md +++ b/content/changelog/2026/04-23-otoroshi-17.15.md @@ -17,19 +17,19 @@ excludeSearch: true [Otoroshi v17.15.1](https://github.com/MAIF/otoroshi/releases/tag/v17.15.1) is available. It introduces an OAuth2 token exchange plugin implementing [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) for secure service-to-service API communication, a new `PluginPreset` kind to package and reuse plugin configurations, a `MandatoryConsumerPreset` plugin and a workflow-based authentication module. The `OAuth2Caller` plugin can now rely on an authentication module for the client credentials flow, and `$2b$` / `$2y$` Bcrypt hashes are now accepted. -### A redesigned admin interface and manual +## A redesigned admin interface and manual The admin interface has been redesigned with refreshed themes, a new home page, a reworked API editor with sticky actions and form views capped at 1000 px to improve readability. The UI also now only displays entities a user is actually authorized to see. The full manual has been reworked as well, with ditaa schemas replaced by mermaid ones, and the OpenAPI specification and Swagger UI can be protected behind an optional access secret. -### API management, data exporters and plugin development +## API management, data exporters and plugin development API management gains direct subscription to a plan and a new consistency service. The PostgreSQL data exporter now supports retention and plugin developers can rely on a reusable stateful clients internal API. Several fixes address a race condition in data exporters loading, legacy audit event layouts, body consumption handling and the `OAuth2Caller` password grant type. -### LLM extension: agents, memory and Kreuzberg +## LLM extension: agents, memory and Kreuzberg This release includes LLM extension [0.0.75](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.75), which significantly expands agent capabilities with built-in tools (`system_exec`, `file_read`, `file_write`, `http_call`), a per-session working memory, autonomous persistent memory on Redis and PostgreSQL and a new agent OpenAI proxy plugin. Embedding stores and persistent memories now support many backends (PostgreSQL, Redis, Elasticsearch, OpenSearch, Qdrant, Weaviate, Pinecone, ChromaDB), a semantic cache on Redis with a custom embedding model and MCP audit events. The Anthropic provider exposes version and beta settings, and [Kreuzberg](https://kreuzberg.dev) is now available as a content-to-markdown and OCR backend. -### Java 25 is now required +## Java 25 is now required Otoroshi 17.15 requires Java 25 to load the Kreuzberg content-to-markdown and OCR backend. Starting with this release, all new Otoroshi add-ons are created with `CC_JAVA_VERSION=25`, and any update to Otoroshi 17.15 automatically switches the underlying Java application to Java 25. If you prefer to handle this manually, you can set `CC_JAVA_VERSION` to `25` on the Java application backing your Otoroshi add-on and rebuild it before updating Otoroshi itself. diff --git a/content/changelog/2026/04-28-images-update.md b/content/changelog/2026/04-28-images-update.md index ae6183706..575e53867 100644 --- a/content/changelog/2026/04-28-images-update.md +++ b/content/changelog/2026/04-28-images-update.md @@ -14,13 +14,13 @@ excludeSearch: true We updated all our images, except PHP. Deployment is in progress for all our users. -* **Common:** - * Linux kernel 6.19.14 - * CA certificates 20260223 - * Git 2.54.0 -* **Node.js & Bun:** - * Yarn 4.14.1 -* **Ruby:** - * Update to 3.2.11 - * Update to 3.4.9 - * Update to 4.0.3 +- **Common:** + - Linux kernel 6.19.14 + - CA certificates 20260223 + - Git 2.54.0 +- **Node.js & Bun:** + - Yarn 4.14.1 +- **Ruby:** + - Update to 3.2.11 + - Update to 3.4.9 + - Update to 4.0.3 diff --git a/content/changelog/2026/05-11-materia-kv-graphql.md b/content/changelog/2026/05-11-materia-kv-graphql.md index a15af9a89..8523014d5 100644 --- a/content/changelog/2026/05-11-materia-kv-graphql.md +++ b/content/changelog/2026/05-11-materia-kv-graphql.md @@ -18,7 +18,7 @@ excludeSearch: true As Materia KV is a distributed cluster, the GraphQL endpoint is a single URL shared by every add-on in a given region. For Paris: -``` +```text https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql ``` diff --git a/content/changelog/2026/05-21-pg-update-18.4.md b/content/changelog/2026/05-21-pg-update-18.4.md index ddaf0ef91..e73b587f3 100644 --- a/content/changelog/2026/05-21-pg-update-18.4.md +++ b/content/changelog/2026/05-21-pg-update-18.4.md @@ -13,14 +13,15 @@ excludeSearch: true --- New PostgreSQL versions are available for new add-ons and migration: -* PostgreSQL 18.4 -* PostgreSQL 17.10 -* PostgreSQL 16.14 -* PostgreSQL 15.18 -* PostgreSQL 14.23 + +- PostgreSQL 18.4 +- PostgreSQL 17.10 +- PostgreSQL 16.14 +- PostgreSQL 15.18 +- PostgreSQL 14.23 These versions include [over 60 bug fixes and improvements](https://www.postgresql.org/about/news/postgresql-184-1710-1614-1518-and-1423-released-3297), with security patches for 11 CVEs: [CVE-2026-6472](https://nvd.nist.gov/vuln/detail/CVE-2026-6472), [CVE-2026-6473](https://nvd.nist.gov/vuln/detail/CVE-2026-6473), [CVE-2026-6474](https://nvd.nist.gov/vuln/detail/CVE-2026-6474), [CVE-2026-6475](https://nvd.nist.gov/vuln/detail/CVE-2026-6475), [CVE-2026-6476](https://nvd.nist.gov/vuln/detail/CVE-2026-6476), [CVE-2026-6477](https://nvd.nist.gov/vuln/detail/CVE-2026-6477), [CVE-2026-6478](https://nvd.nist.gov/vuln/detail/CVE-2026-6478), [CVE-2026-6479](https://nvd.nist.gov/vuln/detail/CVE-2026-6479), [CVE-2026-6575](https://nvd.nist.gov/vuln/detail/CVE-2026-6575), [CVE-2026-6637](https://nvd.nist.gov/vuln/detail/CVE-2026-6637) and [CVE-2026-6638](https://nvd.nist.gov/vuln/detail/CVE-2026-6638). PostgreSQL 14 reaches its end of life on 12 November 2026. Plan a migration to a newer version before that date to keep receiving security updates. Also note that PostgreSQL 11 [is not available anymore](/changelog/2025/03-24-postgresql-11-12-eol/) for migrations on Clever Cloud. -* [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) diff --git a/content/changelog/2026/05-22-images-update.md b/content/changelog/2026/05-22-images-update.md index 889bfb504..822003be5 100644 --- a/content/changelog/2026/05-22-images-update.md +++ b/content/changelog/2026/05-22-images-update.md @@ -14,50 +14,50 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Apache 2.4.67 - * Chromium 148.0.7778.167 - * Clever Tools 4.10.0 - * cURL 8.20.0 - * Mise 2026.5.3 - * NGINX 1.30.1 - * OAuth2 Proxy 7.15.2 - * Redis 8.6.3 - * SQLite 3.53.1 -* **FrankenPHP:** - * Update to 1.12.3 (For `CC_PHP_VERSION=8.5`) -* **Go:** - * Go 1.26.3 -* **Java:** - * Update to 1.8.0.492_p09 - * Update to 11.0.31_p11 - * Update to 17.0.19_p10 - * Update to 21.0.11_p10 - * Update to 25.0.3_p9 - * Gradle 8.14.5 - * Gradle 9.5.1 -* **Node.js & Bun:** - * Bun 1.3.14 -* **PHP:** - * Update to 8.2.31 - * Update to 8.3.31 - * Update to 8.4.21 - * Update to 8.5.6 - * Composer 2.2.27 - * Composer 2.9.7 -* **Python:** - * Update to 3.14.5 - * uv 0.11.15 -* **Ruby:** - * Update to 4.0.5 -* **Scala:** - * Play Framework 1.2.7.2 - * Play Framework 1.11.0 -* **Static:** - * Hugo 0.152.2 - * Hugo 0.159.2 - * Hugo 0.160.1 - * Hugo 0.161.1 +- **Common:** + - Apache 2.4.67 + - Chromium 148.0.7778.167 + - Clever Tools 4.10.0 + - cURL 8.20.0 + - Mise 2026.5.3 + - NGINX 1.30.1 + - OAuth2 Proxy 7.15.2 + - Redis 8.6.3 + - SQLite 3.53.1 +- **FrankenPHP:** + - Update to 1.12.3 (For `CC_PHP_VERSION=8.5`) +- **Go:** + - Go 1.26.3 +- **Java:** + - Update to 1.8.0.492_p09 + - Update to 11.0.31_p11 + - Update to 17.0.19_p10 + - Update to 21.0.11_p10 + - Update to 25.0.3_p9 + - Gradle 8.14.5 + - Gradle 9.5.1 +- **Node.js & Bun:** + - Bun 1.3.14 +- **PHP:** + - Update to 8.2.31 + - Update to 8.3.31 + - Update to 8.4.21 + - Update to 8.5.6 + - Composer 2.2.27 + - Composer 2.9.7 +- **Python:** + - Update to 3.14.5 + - uv 0.11.15 +- **Ruby:** + - Update to 4.0.5 +- **Scala:** + - Play Framework 1.2.7.2 + - Play Framework 1.11.0 +- **Static:** + - Hugo 0.152.2 + - Hugo 0.159.2 + - Hugo 0.160.1 + - Hugo 0.161.1 ## Linux Kernel diff --git a/content/changelog/2026/05-29-otoroshi-17.16.md b/content/changelog/2026/05-29-otoroshi-17.16.md index 81586c3e9..861392902 100644 --- a/content/changelog/2026/05-29-otoroshi-17.16.md +++ b/content/changelog/2026/05-29-otoroshi-17.16.md @@ -17,7 +17,7 @@ excludeSearch: true [Otoroshi v17.16.0](https://github.com/MAIF/otoroshi/releases/tag/v17.16.0) is available. This release focuses on observability, API lifecycle management, Kubernetes deployments and a set of brand-new HTTP standards plugins. It also ships an updated LLM extension featuring an AI Assistant for the admin interface and an updated WAF extension. The Otoroshi documentation has been substantially expanded as well. -### User analytics, dashboards and a global event stream +## User analytics, dashboards and a global event stream Otoroshi gains a complete user analytics stack. A PostgreSQL-based analytics exporter stores and queries events at scale, and you can now build your own dashboards with a graphical widget editor, drill-down support and a set of default dashboards shipped out of the box. User-defined alerts are built from analytics queries through a graphical condition editor, with a dedicated alerting interface and a scheduled evaluation job. Dashboards, alerts and analytics queries are all integrated with RBAC. @@ -25,7 +25,7 @@ A new global node event stream page in the back office streams audit, alert and - [Learn more about dashboards, analytics queries and alerts](https://maif.github.io/otoroshi/manual/docs/topics/user-analytics/) -### API management and new HTTP standards plugins +## API management and new HTTP standards plugins API management hardens the split between draft and production: a clear version banner, read-only production views and plan lifecycle enforcement that locks the `Api` entity fields once a plan is published. A getting-started stepper guides newcomers, and the draft subscription listing, top bar search and API key plan editor have all been cleaned up. @@ -34,21 +34,21 @@ This release introduces three plugins implementing recent HTTP standards. The HT - [HTTP Message Signatures (RFC 9421) tutorial](https://maif.github.io/otoroshi/manual/docs/tutorials/http-message-signatures-rfc9421/) - [Learn more about RFC 9440 Client-Cert headers](https://maif.github.io/otoroshi/manual/docs/topics/tls/#rfc-9440--client-cert--client-cert-chain-headers) -### Distributed rate limiting, mailers and exporters +## Distributed rate limiting, mailers and exporters A new distributed rate-limiting strategy backed by atomic Lua scripts on Redis provides predictable, cluster-aware quotas across nodes. It is a drop-in alternative to the previous distributed strategy and is configurable per route. Scaleway TEM and MailPace join the supported mailers. -### Reworked Kubernetes deployments +## Reworked Kubernetes deployments The Helm chart has been almost entirely rewritten with first-class cluster mode (leader and worker deployments), a Redis secured by default based on the CloudPirates Redis chart, and proper HPA, PDB, NetworkPolicies, RBAC, webhooks and certificate templates. The Kustomize manifests reach feature parity with Helm through a component-based split, and the generated CRDs now expose previously missing entities, including APIs, dashboards, alerts and the new analytics resources. -### LLM extension: an AI Assistant and Meta MCP +## LLM extension: an AI Assistant and Meta MCP This release includes LLM extension [0.0.76](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.76), which adds an AI Assistant to the Otoroshi admin interface. The assistant can search the documentation, query the admin API and run administrative tasks through its Search, Doc and Execute tools, with a conversational chat window, streaming responses, light and dark themes and API key-based access. A hybrid semantic and lexical search engine backs documentation queries. The extension also introduces a Meta MCP connector that aggregates and proxies multiple MCP servers with dynamic capabilities, an Otoroshi MCP server plugin exposing the assistant tools, and image generation support in the `Response` and `OpenResponse` plugins. The Monaco editor is now used across all administration pages of the extension. -### Updated bundled plugins +## Updated bundled plugins Every plugin delivered with Otoroshi on Clever Cloud has been rebuilt against 17.16.0. The WAF extension ([0.0.9](https://github.com/cloud-apim/otoroshi-waf-extension/releases/tag/0.0.9)) upgrades the OWASP Core Rule Set to v4.26.0. diff --git a/content/changelog/2026/06-17-images-update.md b/content/changelog/2026/06-17-images-update.md index 5a9c21fc1..ae26bf9c4 100644 --- a/content/changelog/2026/06-17-images-update.md +++ b/content/changelog/2026/06-17-images-update.md @@ -14,39 +14,39 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Apache 2.4.68 - * Chromium 149.0.7827.114 - * Mise 2026.6.0 - * NGINX 1.30.2 - * OAuth2 Proxy 7.15.3 - * Redis 8.8.0 -* **Docker:** - * Update to 29.5.2 -* **Erlang/Elixir:** - * Rebar 3.27.0 -* **FrankenPHP:** - * Update to 1.12.4 (For `CC_PHP_VERSION=8.5`) -* **Go:** - * Go 1.26.4 -* **Node.js & Bun:** - * Yarn 4.16.0 -* **PHP:** - * Update to 8.4.22 - * Update to 8.5.7 - * Composer 1.20.28 - * Composer 2.2.28 - * Composer 2.10.1 -* **Python:** - * Update to 3.13.14 - * Update to 3.14.6 - * pip 26.1.2 - * uv 0.11.21 -* **Rust:** - * Rust 1.96.0 -* **Static:** - * Hugo 0.162.1 - * Static Web Server 2.43.0 +- **Common:** + - Apache 2.4.68 + - Chromium 149.0.7827.114 + - Mise 2026.6.0 + - NGINX 1.30.2 + - OAuth2 Proxy 7.15.3 + - Redis 8.8.0 +- **Docker:** + - Update to 29.5.2 +- **Erlang/Elixir:** + - Rebar 3.27.0 +- **FrankenPHP:** + - Update to 1.12.4 (For `CC_PHP_VERSION=8.5`) +- **Go:** + - Go 1.26.4 +- **Node.js & Bun:** + - Yarn 4.16.0 +- **PHP:** + - Update to 8.4.22 + - Update to 8.5.7 + - Composer 1.20.28 + - Composer 2.2.28 + - Composer 2.10.1 +- **Python:** + - Update to 3.13.14 + - Update to 3.14.6 + - pip 26.1.2 + - uv 0.11.21 +- **Rust:** + - Rust 1.96.0 +- **Static:** + - Hugo 0.162.1 + - Static Web Server 2.43.0 ## Hugo version update diff --git a/content/changelog/2026/06-30-otoroshi-17.17.md b/content/changelog/2026/06-30-otoroshi-17.17.md index f130e33bb..d97462eb1 100644 --- a/content/changelog/2026/06-30-otoroshi-17.17.md +++ b/content/changelog/2026/06-30-otoroshi-17.17.md @@ -14,21 +14,21 @@ excludeSearch: true [Otoroshi v17.17.0](https://github.com/MAIF/otoroshi/releases/tag/v17.17.0) is available on Clever Cloud. This release adds standards-based token introspection, hardens API key validation and improves Elasticsearch compatibility. It also ships LLM extension [0.0.82](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.82), which builds on [0.0.81](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.81) and [0.0.80](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.80). -### RFC 7662 token introspection and admin bootstrap +## RFC 7662 token introspection and admin bootstrap Otoroshi now implements the [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662) introspection flow, so you can validate opaque access tokens against an introspection endpoint. The `OIDCJwtVerifier` also reads the audience from an array, aligning it with providers that emit `aud` as a list. The initial admin password can now be generated and stored in a temporary file at first startup, which makes automated and reproducible bootstrap of a fresh instance easier. -### Hardened API key validation +## Hardened API key validation Several fixes tighten how API keys are accepted. Disabled API keys are no longer accepted in client-id-only mode, and a bearer signature validation bypass in that same mode has been corrected. Legacy quota declarations are now reported on the API key quotas endpoint, so quota reporting stays consistent across key formats. -### Elasticsearch 8 and Expression Language +## Elasticsearch 8 and Expression Language Writes to Elasticsearch 8 no longer include the deprecated `_type` field, and the cluster version can be auto-filled from the interface. The Expression Language handles default values more reliably and fixes the ordering of `ctx.geolocation.*` and `ctx.useragent.*` expressions. The `NgErrorRewriter` plugin also receives several improvements, and ingress fetching now targets the correct API group. -### LLM extension: A2A, MCP virtual servers and gateway discovery +## LLM extension: A2A, MCP virtual servers and gateway discovery The bundled LLM extension reaches [0.0.82](https://github.com/cloud-apim/otoroshi-llm-extension/releases/tag/0.0.82) and includes the features introduced in 0.0.81 and 0.0.80. The provider and capability catalogs are now exposed through the authenticated admin API, and a new `RampartBodyRedactionBackend` terminal plugin reads the request body, redacts PII with the local Rampart model and returns the redacted body directly. The Mistral provider adopts the OpenAI content representation for chat messages, improving support for structured and multi-part content. diff --git a/content/changelog/2026/07-01-images-update.md b/content/changelog/2026/07-01-images-update.md index 73a5464e9..ee5ddb5d3 100644 --- a/content/changelog/2026/07-01-images-update.md +++ b/content/changelog/2026/07-01-images-update.md @@ -14,20 +14,20 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * cURL 8.21.0 - * Mise 2026.6.14 - * NGINX 1.30.3 -* **Docker:** - * Update to 29.6.1 -* **Java:** - * Gradle 9.6.1 -* **PHP:** - * `mod_ssl` module is now loaded -* **Python:** - * uv 0.11.25 -* **Static:** - * Hugo 0.163.3 +- **Common:** + - cURL 8.21.0 + - Mise 2026.6.14 + - NGINX 1.30.3 +- **Docker:** + - Update to 29.6.1 +- **Java:** + - Gradle 9.6.1 +- **PHP:** + - `mod_ssl` module is now loaded +- **Python:** + - uv 0.11.25 +- **Static:** + - Hugo 0.163.3 ## Hugo version update diff --git a/content/changelog/2026/07-28-images-update.md b/content/changelog/2026/07-28-images-update.md index e0e0c5696..89393b523 100644 --- a/content/changelog/2026/07-28-images-update.md +++ b/content/changelog/2026/07-28-images-update.md @@ -14,43 +14,42 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Chromium 150.0.7871.186 - * Clever Tools 4.11.0 - * Git 2.55.0 - * Nano 9.1 - * NGINX 1.30.4 - * OpenSSH 10.4_p1 - * Perl 5.44.0 - * Poppler 26.07.0 - * Zellij 0.43.1 -* **Docker:** - * Update to 29.6.2 -* **Go:** - * Go 1.26.5 -* **Node.js & Bun:** - * Node.js 24.18.0 (npm 11.16.0) - * Yarn 4.17.1 -* **Java:** - * Update to 11.0.32_p9 - * Update to 21.0.12_p8 -* **PHP:** - * Update to 8.2.32 - * Update to 8.3.32 - * Update to 8.4.23 - * Update to 8.5.8 - * Composer 2.10.2 - * Elastic APM agent 1.17.0 - * New Relic extension 12.8.0.37 -* **Python:** - * uv 0.11.29 -* **Rust:** - * Update to 1.97.1 -* **Static:** - * Caddy 2.11.4 - * Hugo 0.164.0 +- **Common:** + - Chromium 150.0.7871.186 + - Clever Tools 4.11.0 + - Git 2.55.0 + - Nano 9.1 + - NGINX 1.30.4 + - OpenSSH 10.4_p1 + - Perl 5.44.0 + - Poppler 26.07.0 + - Zellij 0.43.1 +- **Docker:** + - Update to 29.6.2 +- **Go:** + - Go 1.26.5 +- **Node.js & Bun:** + - Node.js 24.18.0 (npm 11.16.0) + - Yarn 4.17.1 +- **Java:** + - Update to 11.0.32_p9 + - Update to 21.0.12_p8 +- **PHP:** + - Update to 8.2.32 + - Update to 8.3.32 + - Update to 8.4.23 + - Update to 8.5.8 + - Composer 2.10.2 + - Elastic APM agent 1.17.0 + - New Relic extension 12.8.0.37 +- **Python:** + - uv 0.11.29 +- **Rust:** + - Update to 1.97.1 +- **Static:** + - Caddy 2.11.4 + - Hugo 0.164.0 ## Linux Kernel Kernel is [now updated independently](/changelog/2026/05-12-linux-kernel-7.0.6). Current version is 7.1.5. - diff --git a/content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md b/content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md index 45f3ddf03..01cfd4e7e 100644 --- a/content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md +++ b/content/changelog/2026/07-29-mysql-8.0.46-8.4.10.md @@ -18,6 +18,6 @@ The 8.4 release addresses eight security vulnerabilities: [CVE-2026-46850](https MySQL 8.0 [reached its end of life on 30 April 2026](https://www.mysql.com/support/eol-notice.html), and 8.0.46-37 is [the final release of the Percona Server 8.0 series](https://docs.percona.com/new/2026/06/10/percona-server-for-mysql-8046-37-has-been-released/). If you run a MySQL 8.0 add-on, we recommend you migrate it to the 8.4 LTS branch. -* [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) -* [Learn more about MySQL 8.0.46](https://docs.percona.com/percona-server/8.0/release-notes/8.0.46-37.html) -* [Learn more about MySQL 8.4.10](https://docs.percona.com/percona-server/8.4/release-notes/8.4.10-10.html) +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 8.0.46](https://docs.percona.com/percona-server/8.0/release-notes/8.0.46-37.html) +- [Learn more about MySQL 8.4.10](https://docs.percona.com/percona-server/8.4/release-notes/8.4.10-10.html) diff --git a/content/changelog/2026/08-05-images-update.md b/content/changelog/2026/08-05-images-update.md index 2a0daf17e..e3942833f 100644 --- a/content/changelog/2026/08-05-images-update.md +++ b/content/changelog/2026/08-05-images-update.md @@ -14,31 +14,31 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Chromium 151.0.7922.71 - * Redis 8.10.0 - * Ripgrep 15.2.0 -* **.NET:** - * Update to 8.0.129 - * Update to 9.0.119 - * Update to 10.0.110 -* **Java:** - * Update to 1.8.0.502_p07 - * Update to 17.0.20_p8 - * Update to 25.0.4_p7 -* **Node.js & Bun:** - * Node.js 24.18.1 (npm 11.16.0) - * Yarn 4.18.0 -* **PHP:** - * Update to 8.2.33 - * Update to 8.3.33 - * Update to 8.4.24 - * Update to 8.5.9 - * Blackfire agent 2026.7.0 - * Blackfire extension 2026.7.1 -* **Python:** - * pip 26.2 - * uv 0.12.0 +- **Common:** + - Chromium 151.0.7922.71 + - Redis 8.10.0 + - Ripgrep 15.2.0 +- **.NET:** + - Update to 8.0.129 + - Update to 9.0.119 + - Update to 10.0.110 +- **Java:** + - Update to 1.8.0.502_p07 + - Update to 17.0.20_p8 + - Update to 25.0.4_p7 +- **Node.js & Bun:** + - Node.js 24.18.1 (npm 11.16.0) + - Yarn 4.18.0 +- **PHP:** + - Update to 8.2.33 + - Update to 8.3.33 + - Update to 8.4.24 + - Update to 8.5.9 + - Blackfire agent 2026.7.0 + - Blackfire extension 2026.7.1 +- **Python:** + - pip 26.2 + - uv 0.12.0 ## Linux Kernel diff --git a/content/changelog/2026/08-19-images-update.md b/content/changelog/2026/08-19-images-update.md index 4477cafc2..376fb637d 100644 --- a/content/changelog/2026/08-19-images-update.md +++ b/content/changelog/2026/08-19-images-update.md @@ -14,31 +14,31 @@ excludeSearch: true We updated all our images. Deployment is in progress for all our users. -* **Common:** - * Anubis 1.26.2 - * ClamAV 1.5.4 - * Chromium 151.0.7922.137 - * FFmpeg 9.0 - * Nano 9.2 - * OpenSSH 10.5_p1 - * Poppler 26.08.0 - * pdfio 1.6.4 is now available - * qpdf is no longer included - * rsync 3.5.0 -* **Go:** - * Update to 1.26.6 -* **Node.js & Bun:** - * Node.js 24.19.0 (npm 11.17.0) -* **Python:** - * Update to 3.10.21 - * Update to 3.11.16 - * Update to 3.12.14 - * Update to 3.13.15 - * Update to 3.14.7 -* **Ruby:** - * Update to 3.3.12 - * Update to 3.4.10 - * Update to 4.0.6 +- **Common:** + - Anubis 1.26.2 + - ClamAV 1.5.4 + - Chromium 151.0.7922.137 + - FFmpeg 9.0 + - Nano 9.2 + - OpenSSH 10.5_p1 + - Poppler 26.08.0 + - pdfio 1.6.4 is now available + - qpdf is no longer included + - rsync 3.5.0 +- **Go:** + - Update to 1.26.6 +- **Node.js & Bun:** + - Node.js 24.19.0 (npm 11.17.0) +- **Python:** + - Update to 3.10.21 + - Update to 3.11.16 + - Update to 3.12.14 + - Update to 3.13.15 + - Update to 3.14.7 +- **Ruby:** + - Update to 3.3.12 + - Update to 3.4.10 + - Update to 4.0.6 ## Health Check and Request Flow improvements diff --git a/content/doc/account/create-account.md b/content/doc/account/create-account.md index b9571c3c5..728d907cd 100644 --- a/content/doc/account/create-account.md +++ b/content/doc/account/create-account.md @@ -32,8 +32,8 @@ The GitHub signup allows you to create an account or link your existing one to G This process asks the following permissions: -* Read your Public Key -* Read User Repositories +- Read your Public Key +- Read User Repositories The "repository permission" is used to deploy your GitHub apps directly to Clever Cloud, with a simple step. diff --git a/content/doc/account/notifications.md b/content/doc/account/notifications.md index 38ed82b72..1c7ec7722 100644 --- a/content/doc/account/notifications.md +++ b/content/doc/account/notifications.md @@ -90,20 +90,20 @@ the result of the deployment in your Slack channel. Available webhooks events are organised by category: -| Category | Events | -|----------|--------| -| **Account** | `ACCOUNT_CREATION`, `ACCOUNT_DELETION`, `ACCOUNT_EDITION` | -| **Organisation** | `ORGANISATION_CREATION`, `ORGANISATION_DELETION`, `ORGANISATION_EDITION` | -| **Add-on** | `ADDON_CREATION`, `ADDON_DELETION` | -| **Application** | `APPLICATION_CREATION`, `APPLICATION_DELETION`, `APPLICATION_EDITION`, `APPLICATION_REDEPLOY`, `APPLICATION_STOP` | -| **Deployment** | `DEPLOYMENT_ACTION_BEGIN`, `DEPLOYMENT_ACTION_END`, `DEPLOYMENT_FAIL`, `DEPLOYMENT_SUCCESS`, `GIT_PUSH` | -| **Other** | `CLEVER_TOOLS_REQUEST`, `CREDITS_ADDED` | +| Category | Events | +| ---------------- | ----------------------------------------------------------------------------------------------------------------- | +| **Account** | `ACCOUNT_CREATION`, `ACCOUNT_DELETION`, `ACCOUNT_EDITION` | +| **Organisation** | `ORGANISATION_CREATION`, `ORGANISATION_DELETION`, `ORGANISATION_EDITION` | +| **Add-on** | `ADDON_CREATION`, `ADDON_DELETION` | +| **Application** | `APPLICATION_CREATION`, `APPLICATION_DELETION`, `APPLICATION_EDITION`, `APPLICATION_REDEPLOY`, `APPLICATION_STOP` | +| **Deployment** | `DEPLOYMENT_ACTION_BEGIN`, `DEPLOYMENT_ACTION_END`, `DEPLOYMENT_FAIL`, `DEPLOYMENT_SUCCESS`, `GIT_PUSH` | +| **Other** | `CLEVER_TOOLS_REQUEST`, `CREDITS_ADDED` | Some events are grouped in meta-events for easier selection: -| Meta-Event | Included Events | -|------------|-----------------| -| `META_CREDITS` | `CREDITS_ADDED` | -| `META_DEPLOYMENT_RESULT` | `DEPLOYMENT_FAIL`, `DEPLOYMENT_SUCCESS` | -| `META_SERVICE_LIFECYCLE` | `APPLICATION_STOP`, `DEPLOYMENT_ACTION_BEGIN`, `DEPLOYMENT_FAIL`, `DEPLOYMENT_SUCCESS` | +| Meta-Event | Included Events | +| ------------------------- | --------------------------------------------------------------------------------------------------------- | +| `META_CREDITS` | `CREDITS_ADDED` | +| `META_DEPLOYMENT_RESULT` | `DEPLOYMENT_FAIL`, `DEPLOYMENT_SUCCESS` | +| `META_SERVICE_LIFECYCLE` | `APPLICATION_STOP`, `DEPLOYMENT_ACTION_BEGIN`, `DEPLOYMENT_FAIL`, `DEPLOYMENT_SUCCESS` | | `META_SERVICE_MANAGEMENT` | `ADDON_CREATION`, `ADDON_DELETION`, `APPLICATION_CREATION`, `APPLICATION_EDITION`, `APPLICATION_DELETION` | diff --git a/content/doc/account/organisations.md b/content/doc/account/organisations.md index 94acffba9..2b01a3ae2 100644 --- a/content/doc/account/organisations.md +++ b/content/doc/account/organisations.md @@ -33,14 +33,14 @@ In order to add an organisation, just click on "Add an organisation" in the top The table below describes rights assigned to roles: -Role | Admin | Manager | Developer | Accountant | ------|-------|---------|-----------|------------| -Add Member | ✓ | ✓ | | | -Remove Member | ✓ | ✓ | | | -Add Application | ✓ | ✓ | ✓ | | -Remove Application | ✓ | ✓ | | | -Add / Remove add-on | ✓ | ✓ | | | -Edit organisation | ✓ | ✓ | | | -Delete organisation | ✓ | | | | -Access Bills & Receive Invoices | ✓ | | | ✓ | -Access Repositories | ✓ | ✓ | ✓ | | +| Role | Admin | Manager | Developer | Accountant | +| ------------------------------- | ----- | ------- | --------- | ---------- | +| Add Member | ✓ | ✓ | | | +| Remove Member | ✓ | ✓ | | | +| Add Application | ✓ | ✓ | ✓ | | +| Remove Application | ✓ | ✓ | | | +| Add / Remove add-on | ✓ | ✓ | | | +| Edit organisation | ✓ | ✓ | | | +| Delete organisation | ✓ | | | | +| Access Bills & Receive Invoices | ✓ | | | ✓ | +| Access Repositories | ✓ | ✓ | ✓ | | diff --git a/content/doc/account/ssh-keys-management.md b/content/doc/account/ssh-keys-management.md index 9954b2cc8..d8177a0ac 100644 --- a/content/doc/account/ssh-keys-management.md +++ b/content/doc/account/ssh-keys-management.md @@ -149,7 +149,7 @@ clever curl -X PUT -H "Content-Type: application/json" --data "\"$(cat ~/.ssh/yo If you want to clone a repository from a private repository, you can add a [private SSH key](/doc/reference/common-configuration#private-ssh-key) to an application by creating a folder clevercloud at the root of your application and creating the file `clevercloud/ssh.json` with the following content: -```json{filename="clevercloud/ssh.json"} +```json {filename="clevercloud/ssh.json"} { "privateKeyFile": "path/to/file" } diff --git a/content/doc/addons/cellar.md b/content/doc/addons/cellar.md index cedb35a8a..d495ea348 100644 --- a/content/doc/addons/cellar.md +++ b/content/doc/addons/cellar.md @@ -188,9 +188,10 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou [Bun](https://bun.sh) includes a [native S3 client](https://bun.sh/docs/api/s3) with no external dependency. It works with any S3-compatible service, including Cellar. **Required environment variables:** - - `CELLAR_ADDON_HOST` - Cellar endpoint (e.g., `cellar-c2.services.clever-cloud.com`) - - `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID - - `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key + +- `CELLAR_ADDON_HOST` - Cellar endpoint (e.g., `cellar-c2.services.clever-cloud.com`) +- `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID +- `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key These variables are automatically available in your application when you [link a Cellar add-on](/doc/addons/cellar/) to it on Clever Cloud, regardless of the runtime (for example in a [Node.js & Bun application](/doc/applications/nodejs/)). @@ -236,9 +237,10 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou Using AWS SDK for JavaScript v3 (recommended): **Required environment variables:** - - `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `https://cellar-c2.services.clever-cloud.com`) - - `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID - - `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key + +- `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `https://cellar-c2.services.clever-cloud.com`) +- `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID +- `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key Some SDKs require a region even with a custom endpoint, so set `region` to the literal value "`default`". @@ -303,13 +305,15 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou Make sure to use latest version of the `2.X`, new versions are released regularly. See [the AWS Java SDK Documentation](https://github.com/aws/aws-sdk-java-v2/#using-the-sdk) for more details. **Required environment variables:** - - `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `https://cellar-c2.services.clever-cloud.com`) - - `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID - - `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key + +- `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `https://cellar-c2.services.clever-cloud.com`) +- `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID +- `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key **Optional (for SDK 2.30.0+ checksum compatibility):** - - `AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED` - - `AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED` + +- `AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED` +- `AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED` Some SDKs require a region even with a custom endpoint, so just provide the literal value "`default`" to satisfy that requirement. @@ -375,9 +379,10 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou This script uses boto3, the AWS SDK for Python. **Required environment variables:** - - `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `https://cellar-c2.services.clever-cloud.com`) - - `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID - - `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key + +- `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `https://cellar-c2.services.clever-cloud.com`) +- `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID +- `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key Some SDKs require a region even with a custom endpoint, so set the `region_name` to the literal value "`default`" when instantiating the client. @@ -430,25 +435,26 @@ You only need to specify a custom endpoint (e.g. `cellar-c2.services.clever-clou you must configure a S3 service with a custom endpoint. **Required environment variables:** - - `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `cellar-c2.services.clever-cloud.com`) - - `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID - - `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key + +- `CELLAR_ADDON_HOST` - Cellar endpoint URL (e.g., `cellar-c2.services.clever-cloud.com`) +- `CELLAR_ADDON_KEY_ID` - Your Cellar access key ID +- `CELLAR_ADDON_KEY_SECRET` - Your Cellar secret access key Use this configuration in your `config/storage.yml`: - ```yaml {filename="config/storage.yml"} - cellar: - service: S3 - access_key_id: <%= ENV.fetch('CELLAR_ADDON_KEY_ID') %> - secret_access_key: <%= ENV.fetch('CELLAR_ADDON_KEY_SECRET') %> - endpoint: https://<%= ENV.fetch('CELLAR_ADDON_HOST') %> - region: 'us-west-1' - force_path_style: true - bucket: mybucket - # If experiencing checksum validation errors, add: - # request_checksum_calculation: 'when_required' - # response_checksum_validation: 'when_required' - ``` +```yaml {filename="config/storage.yml"} +cellar: + service: S3 + access_key_id: <%= ENV.fetch('CELLAR_ADDON_KEY_ID') %> + secret_access_key: <%= ENV.fetch('CELLAR_ADDON_KEY_SECRET') %> + endpoint: https://<%= ENV.fetch('CELLAR_ADDON_HOST') %> + region: 'us-west-1' + force_path_style: true + bucket: mybucket + # If experiencing checksum validation errors, add: + # request_checksum_calculation: 'when_required' + # response_checksum_validation: 'when_required' +``` Although the `region` parameter appears, it's not used by Cellar. The region value serves to satisfy ActiveStorage and the aws-sdk-s3 gem. Without a region option, an exception would raise : `missing keyword: region (ArgumentError)`. If region is an empty string you will get the following error: `missing region; use :region option or export region name to ENV['AWS_REGION'] (Aws::Errors::MissingRegionError)`. @@ -513,7 +519,6 @@ s3cmd delpolicy s3:// The original ACL should apply to all of your objects after that. - ### IP restrictions If you need to restrict your S3 Cellar to certain IPs, you can use a policy. @@ -553,12 +558,14 @@ To do so, you can use the template below in a `policy.json` file. This example s ``` To apply the policy, use this command: -``` + +```text s3cmd setpolicy ./policy.json s3:// ``` To delete the policy, use this command: -``` + +```text s3cmd delpolicy ./policy.json s3:// ``` @@ -696,9 +703,9 @@ Only the latest version of a file is visible, while previous versions remain hid Versioning can't be enabled for a single file: it will cover the entire bucket. A bucket can be in one of three state : - - Unversioned (The default) - - Versioning-enabled - - Versioning-suspended +- Unversioned (The default) +- Versioning-enabled +- Versioning-suspended You enable and suspend versioning at the bucket level. Once you enable versioning on a bucket, it can never return to an unversioned state. But you can suspend versioning on that bucket at any time. Once versioning is enabled, any object you add have a unique version ID. Object that were already existing before enabling versioning have a version ID of `null`. @@ -718,7 +725,7 @@ For that reason, we do recommend you to enable versioning when creating a new bu mc alias set https://cellar-c2.services.clever-cloud.com ``` - ### Activate versioning with MinIO +### Activate versioning with MinIO To activate versioning, you can use MinIO and the following command. @@ -734,11 +741,11 @@ For that reason, we do recommend you to enable versioning when creating a new bu mc version info / --json ``` - ### How to use versioning +### How to use versioning with AWS CLI When versioning is enabled, the newly added object is automatically provided with a unique identifier. Only the latest version of an object is shown with a `mc ls /`. - #### List versioned objects +#### List versioned objects If you need to list all the object in your bucket, including the different versions of the files stored in it, you can run : @@ -752,7 +759,7 @@ When versioning is enabled, the newly added object is automatically provided wit mc ls --versions // ``` - #### Get a version of an object +#### Get a version of an object You can get the specific version of an object using its version ID obtained using the previous command. @@ -760,7 +767,7 @@ When versioning is enabled, the newly added object is automatically provided wit mc get --vid // ``` - #### Delete a version of an object +#### Delete a version of an object You can delete the specific version of an object using its version ID. @@ -768,14 +775,13 @@ When versioning is enabled, the newly added object is automatically provided wit mc rm --vid // ``` - {{< /tab >}} {{< tab name="AWS CLI" icon="aws" >}} The following command assumes you have configured your AWS CLI and added an alias as shown earlier in the section [Creating a bucket with AWS CLI](/doc/addons/cellar/#with-aws-cli) - ### Activate versioning with AWS CLI +### Activate versioning with AWS CLI To activate versioning, you can use AWS CLI. You can use the following command to enable it on a bucket. @@ -795,11 +801,11 @@ When versioning is enabled, the newly added object is automatically provided wit aws s3api get-bucket-versioning --bucket ``` - ### How to use versioning +### How to use versioning When versioning is enabled, the added object automatically gets a versionID. Only the latest version of an object if shown. - #### List all versioned Object +#### List all versioned Object If you need to list all the objects in your bucket, including the different versions of the files stored in it, you can use : @@ -807,7 +813,7 @@ When versioning is enabled, the newly added object is automatically provided wit aws s3api list-object-versions --bucket ``` - #### List all the version of a specific Object +#### List all the version of a specific Object If you want to check the different versions of a specific object, you can use the following command. In this example, we find the versions of test.txt : @@ -815,7 +821,7 @@ When versioning is enabled, the newly added object is automatically provided wit aws s3api list-object-versions --bucket --prefix ``` - #### Get the version of an Object +#### Get the version of an Object If you want to retrieve the previous version of an object, you need the versionID that you can get with any of the two previous command. You can use : @@ -823,7 +829,7 @@ When versioning is enabled, the newly added object is automatically provided wit aws s3api get-object --bucket --version-id '' --key /path/to/save/file/copy/test.txt ``` - #### Delete the version of an object +#### Delete the version of an object To remove a version of an object, you can use this command : diff --git a/content/doc/addons/config-provider.md b/content/doc/addons/config-provider.md index 43a4a82fc..8579678d5 100644 --- a/content/doc/addons/config-provider.md +++ b/content/doc/addons/config-provider.md @@ -25,15 +25,15 @@ Let's take an example with 2 Node.js applications exposing some REST APIs: `ship The `shipping-api` application needs the following environment variables: -* `PULSAR_TOPIC=order` -* `USER_API_HOST=https://user.example.com/api` -* `ENABLE_PARTNERS=true` +- `PULSAR_TOPIC=order` +- `USER_API_HOST=https://user.example.com/api` +- `ENABLE_PARTNERS=true` The `payment-api` application needs the following environment variables: -* `PULSAR_TOPIC=order` -* `USER_API_HOST=https://user.example.com/api` -* `ENABLE_SEPA=false` +- `PULSAR_TOPIC=order` +- `USER_API_HOST=https://user.example.com/api` +- `ENABLE_SEPA=false` ![Two Node.js applications with their own environment variables](/images/schema-config-provider-01.svg "Two Node.js applications with their own environment variables") @@ -72,10 +72,10 @@ At any point, you can link/unlink an application from the configuration provider ## Details and behaviour -* As with any other add-on, you can only link a configuration provider to applications that are in the same organisation. -* Variables defined directly in the application override the ones injected from the configuration provider. -* When you update the variables of a configuration provider, all applications linked to it will be automatically restarted. -* You can create multiple configuration providers in your organisation and link them to different applications. +- As with any other add-on, you can only link a configuration provider to applications that are in the same organisation. +- Variables defined directly in the application override the ones injected from the configuration provider. +- When you update the variables of a configuration provider, all applications linked to it will be automatically restarted. +- You can create multiple configuration providers in your organisation and link them to different applications. ## Pricing diff --git a/content/doc/addons/elastic.md b/content/doc/addons/elastic.md index 6e5476788..cd07a9d01 100644 --- a/content/doc/addons/elastic.md +++ b/content/doc/addons/elastic.md @@ -81,7 +81,7 @@ You can change the URL to point to your own custom configuration. Here is a configuration example for RUM activation: -```bash{filename="es-apm-serverconfig.sh"} +```bash {filename="es-apm-serverconfig.sh"} #!/bin/bash -l cat <apm-server.yml @@ -132,21 +132,21 @@ The agent will periodically scan for JVM processes and will attach to them if ne Elasticsearch managed by Clever Cloud comes with these plugins, you can activate at add-on creation: -| Plugin | Description | -| ------ | ----------- | -| analysis-icu | Adds extended Unicode support using the ICU libraries | -| analysis-kuromoji | Advanced analysis of Japanese using the Kuromoji analyzer | -| analysis-nori | Morphological analysis of Korean using the Lucene Nori analyzer | -| analysis-phonetic | Analyzes tokens into their phonetic equivalent using Soundex, Metaphone, Caverphone, and other codecs | -| analysis-smartcn | An analyzer for Chinese or mixed Chinese-English text | -| analysis-stempel | Provides high quality stemming for Polish | -| analysis-ukrainian | Provides stemming for Ukrainian | -| discovery-ec2 | Uses the AWS API to identify the addresses of seed hosts | -| discovery-azure-classic | Uses the Azure Classic API to identify the addresses of seed hosts | -| discovery-gce | Uses the GCE API to identify the addresses of seed hosts | -| mapper-size | Provides the `_size` metadata field which, when enabled, indexes the size in bytes of the original `_source` field | -| mapper-murmur3 | Allows hashes to be computed at index-time and stored in the index for later use with the cardinality aggregation | -| mapper-annotated-text | Provides the ability to index text that is a combination of free-text and special markup | +| Plugin | Description | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------ | +| analysis-icu | Adds extended Unicode support using the ICU libraries | +| analysis-kuromoji | Advanced analysis of Japanese using the Kuromoji analyzer | +| analysis-nori | Morphological analysis of Korean using the Lucene Nori analyzer | +| analysis-phonetic | Analyzes tokens into their phonetic equivalent using Soundex, Metaphone, Caverphone, and other codecs | +| analysis-smartcn | An analyzer for Chinese or mixed Chinese-English text | +| analysis-stempel | Provides high quality stemming for Polish | +| analysis-ukrainian | Provides stemming for Ukrainian | +| discovery-ec2 | Uses the AWS API to identify the addresses of seed hosts | +| discovery-azure-classic | Uses the Azure Classic API to identify the addresses of seed hosts | +| discovery-gce | Uses the GCE API to identify the addresses of seed hosts | +| mapper-size | Provides the `_size` metadata field which, when enabled, indexes the size in bytes of the original `_source` field | +| mapper-murmur3 | Allows hashes to be computed at index-time and stored in the index for later use with the cardinality aggregation | +| mapper-annotated-text | Provides the ability to index text that is a combination of free-text and special markup | {{< callout type="info" >}} Plugin activation is only available at add-on creation, through [API](/api) or the `--option` flag of [Clever Tools](/doc/cli/addons/#create--rename--delete). You must pass the option as a comma-separated list: `plugins=plugin1,plugin2`. To modify enabled plugins on an existing add-on, contact [support team](https://console.clever-cloud.com/ticket-center-choice). diff --git a/content/doc/addons/fs-bucket.md b/content/doc/addons/fs-bucket.md index 7039c99ca..1734d2bf7 100644 --- a/content/doc/addons/fs-bucket.md +++ b/content/doc/addons/fs-bucket.md @@ -98,12 +98,12 @@ You can find a pre-filled json object to copy in the dashboard of your FSBucket It's a json array containing objects with at least two fields: -Usage | Field | Description ----------|--------------|-------------------------------------------------------------- -Required | bucket | The bucket id you can find in the console. It begins with `bucket_`. This is for "old-style" buckets (created before the 7 December 2015) -Required | bucket_host | The bucket host you can find in the console. It begins with `bucket-` and ends with `services.clever-cloud.com`. This is for "new-style" buckets. -Required | folder | The folder you want the bucket to be mounted in. Should start with `/`. Using the example *myFolder*, you can access your bucket via the *myFolder* folder at the root of your application (which absolute path is available in the `APP_HOME` environment variable) -Optional | apps | Whitelist of the applications allowed to mount this bucket. It's helpful if you need to deploy a *preprod* app and a *prod* app using the exact same codebase but different buckets +| Usage | Field | Description | +| -------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Required | bucket | The bucket id you can find in the console. It begins with `bucket_`. This is for "old-style" buckets (created before the 7 December 2015) | +| Required | bucket_host | The bucket host you can find in the console. It begins with `bucket-` and ends with `services.clever-cloud.com`. This is for "new-style" buckets. | +| Required | folder | The folder you want the bucket to be mounted in. Should start with `/`. Using the example *myFolder*, you can access your bucket via the *myFolder* folder at the root of your application (which absolute path is available in the `APP_HOME` environment variable) | +| Optional | apps | Whitelist of the applications allowed to mount this bucket. It's helpful if you need to deploy a *preprod* app and a *prod* app using the exact same codebase but different buckets | The folder must not exist in your repository (or it needs to be empty). Otherwise, the mount of your bucket will be ignored. You can mount the same bucket in different folders, but they will share the same content, so it's not the solution. You should prefer to mount the bucket in only one folder and then manage multiple subfolders in it. @@ -131,8 +131,8 @@ in the FS bucket. The **Add-on information** tab of your FS Bucket in [Clever Cloud Console](https://console.clever-cloud.com) displays information you need to connect using SSH File Transfer Protocol (SFTP). You can use the following SSH public keys to ensure the connection is authentic and trusted: -| Algorithm | Key size | Fingerprint | -|-----------|----------|----------------------------------------------------| +| Algorithm | Key size | Fingerprint | +| --------- | -------- | ---------------------------------------------------- | | ED25519 | 256 | `SHA256:+ku6hhQb1O3OVzkZa2B+htPD+P+5K/X6QQYWXym/4Zo` | | ED25519 | 256 | `SHA256:8tZzRvA3Fh9poG7g1bu8m0LQS819UBh7AYcEXJYiPqw` | | ED25519 | 256 | `SHA256:HHGCP5cf0jQbQrIRXjiC9aYJGNQ+L9ijOmJUueLp+9A` | diff --git a/content/doc/addons/heptapod.md b/content/doc/addons/heptapod.md index 0468e637a..5c8ff3fe4 100644 --- a/content/doc/addons/heptapod.md +++ b/content/doc/addons/heptapod.md @@ -31,18 +31,18 @@ Clever Cloud users can benefit from **Heptapod Cloud** software, along with **He Heptapod Cloud comes with the following benefits: -* **Cost Reduction**: No licensing or application maintenance costs, flexible and competitive usage-based pricing per active user. -* **Automatic Updates**: Heptapod aligns with the continuously evolving GitLab, ensuring you effortlessly benefit from the latest features and security patches. -* **Technical and Functional Support**: Whether for technical or functional questions, support teams are ready to answer all your queries. -* **Scalability**: As your team grows or you manage dozens or hundreds of small or large projects for testing and deployment, Heptapod Cloud is designed to handle it. Increase capacities or quantities of CI/CD runners associated with your projects, those of runtimes, storage, or databases managed by Clever Cloud, or adjust bandwidth to handle heavy loads. All these services are native and structured to meet your scaling needs with ease. +- **Cost Reduction**: No licensing or application maintenance costs, flexible and competitive usage-based pricing per active user. +- **Automatic Updates**: Heptapod aligns with the continuously evolving GitLab, ensuring you effortlessly benefit from the latest features and security patches. +- **Technical and Functional Support**: Whether for technical or functional questions, support teams are ready to answer all your queries. +- **Scalability**: As your team grows or you manage dozens or hundreds of small or large projects for testing and deployment, Heptapod Cloud is designed to handle it. Increase capacities or quantities of CI/CD runners associated with your projects, those of runtimes, storage, or databases managed by Clever Cloud, or adjust bandwidth to handle heavy loads. All these services are native and structured to meet your scaling needs with ease. Heptapod Cloud provides your team with a suite of collaborative tools for software or document production management: -* **Team, Collaborators, and Access Rights:** Add and manage team members, organise roles and access rights based on projects. -* **Git and Mercurial Repository Management:** Heptapod provides a robust and intuitive repository management system for Git and Mercurial, allowing you to effortlessly create, manage, and collaborate on your codebase or documents. With advanced version control and branch management features, you can easily track changes, merge your code, and maintain the integrity and history of your private or public projects. -* **Project Management and Ticket Tracking:** The ticket tracking system allows you to efficiently manage tasks, incidents, and collaborate seamlessly with your team. Stay organized by setting milestones, qualifying with labels, and customising workflows for advanced control of your operations. -* **Collaboration and Code Review:** Heptapod provides a wide range of features for effective collaboration, code or document reviews, online comments, and real-time collaboration tools. Work seamlessly with your team, track exchanges, merge requests, and improve the overall quality of your projects. -* **Embedded Wiki and Documentation:** Knowledge sharing is crucial for high-performing teams. Easily create and maintain comprehensive documentation for your projects, centralise and share knowledge, and provide a smooth experience for new collaborators. +- **Team, Collaborators, and Access Rights:** Add and manage team members, organise roles and access rights based on projects. +- **Git and Mercurial Repository Management:** Heptapod provides a robust and intuitive repository management system for Git and Mercurial, allowing you to effortlessly create, manage, and collaborate on your codebase or documents. With advanced version control and branch management features, you can easily track changes, merge your code, and maintain the integrity and history of your private or public projects. +- **Project Management and Ticket Tracking:** The ticket tracking system allows you to efficiently manage tasks, incidents, and collaborate seamlessly with your team. Stay organized by setting milestones, qualifying with labels, and customising workflows for advanced control of your operations. +- **Collaboration and Code Review:** Heptapod provides a wide range of features for effective collaboration, code or document reviews, online comments, and real-time collaboration tools. Work seamlessly with your team, track exchanges, merge requests, and improve the overall quality of your projects. +- **Embedded Wiki and Documentation:** Knowledge sharing is crucial for high-performing teams. Easily create and maintain comprehensive documentation for your projects, centralise and share knowledge, and provide a smooth experience for new collaborators. ## Accessing Heptapod Cloud @@ -87,10 +87,10 @@ Fully integrated with Heptapod Cloud, the service can also be used from a self-m Clever Cloud Runners for Heptapod comes with the following features: -* **Quick activation:** Activate Clever Cloud Runners for Heptapod for your organisation with just one click. -* **Easy customization:** Easily define your pipelines using templates for the most popular languages. Edit and validate definitions from the interface or directly from the project file. -* **Integrated tracking:** Monitor the execution of your tasks in real time, receive notifications of pipeline success or failure, and view activity reports online. -* **[Components Catalog](/doc/ci-cd/gitlab/#deploy-directly-from-heptapod):** Connect your repository to Clever Cloud and deploy on production, create review apps, and automate tasks. +- **Quick activation:** Activate Clever Cloud Runners for Heptapod for your organisation with just one click. +- **Easy customization:** Easily define your pipelines using templates for the most popular languages. Edit and validate definitions from the interface or directly from the project file. +- **Integrated tracking:** Monitor the execution of your tasks in real time, receive notifications of pipeline success or failure, and view activity reports online. +- **[Components Catalog](/doc/ci-cd/gitlab/#deploy-directly-from-heptapod):** Connect your repository to Clever Cloud and deploy on production, create review apps, and automate tasks. #### Provisioning @@ -114,10 +114,10 @@ This is nothing but the standard GitLab mechanism. If you maintain your own Grou In order to have the Clever Cloud Runner take your jobs, simply activate the shared runners on the wished Groups and Projects: -* Navigate to the Group Settings CI/CD page. -* Expand the Runners section. -* You will be able to activate the shared runners directly from there or allow Projects or sub-Groups to override the Group setting in case you don't want to activate on the full Group. -* Always check the resulting configuration in the Projects Settings CI/CD pages, and correct it if needed. Changes in the Group Settings are not always immediately taken into account on enclosed Projects. +- Navigate to the Group Settings CI/CD page. +- Expand the Runners section. +- You will be able to activate the shared runners directly from there or allow Projects or sub-Groups to override the Group setting in case you don't want to activate on the full Group. +- Always check the resulting configuration in the Projects Settings CI/CD pages, and correct it if needed. Changes in the Group Settings are not always immediately taken into account on enclosed Projects. #### Running only the Clever Cloud Runner @@ -178,7 +178,7 @@ tests: ##### Differences with a Heptapod Runner using the standard Docker executor -**Automatic Dependency Proxy** +###### Automatic Dependency Proxy The Dependency Proxy is a standard GitLab Free feature that provides transparent caching of Docker Hub images to minimize bandwidth and avoid rate limiting problems. @@ -186,7 +186,8 @@ tests: The automatic Dependency Proxy should be mostly transparent to users, except in case of services using a namespaced Docker image and not having an explicit alias. For these, we provide a single service network name whereas the standard executor provides two (replacing forward slashes either by double underscores or dashes). We chose the dash-based RFC compliant one. In short, use octobus-heptapod to reach a service whose Docker image is octobus/heptapod:sometag, or better, set your own explicit alias. -**Job cache** +###### Job cache + Currently, the job cache is discarded at the end of each job. This is because we don't have offloading capabilities for the cache yet, and the entire virtual machine gets decommissioned at the end of the job. @@ -210,7 +211,7 @@ In other words, one size does not fit all. Jobs executed by the Clever Cloud Runner can specify the scale (flavour) of the Docker host it needs, using the `CI_CLEVER_CLOUD_FLAVOR` variable. -```yaml{filename=".gitlab-ci.yml"} +```yaml {filename=".gitlab-ci.yml"} small-job: variables: CI_CLEVER_CLOUD_FLAVOR: XS diff --git a/content/doc/addons/keycloak.md b/content/doc/addons/keycloak.md index 32077077f..c40bbbb5a 100644 --- a/content/doc/addons/keycloak.md +++ b/content/doc/addons/keycloak.md @@ -25,7 +25,9 @@ To fit the most common needs, the Keycloak add-on comes with batteries included: {{< callout type="info" >}}Share your feedback on Keycloak operator through [our community page](https://github.com/CleverCloud/Community/discussions/categories/keycloak){{< /callout >}} ## Key features + Keycloak on Clever Cloud allows you to effortlessly set up a tailored authentication and access management solution, that you can adjust to your needs and workloads. It offers a wide panel of services such as: + - Secure Identity Management - Single Sign-On (SSO) - Centralized Administration @@ -41,6 +43,7 @@ Keycloak on Clever Cloud allows you to effortlessly set up a tailored authentica The Clever Cloud Keycloak add-on is designed to meet the most common needs, built with [Please Open-it](https://please-open.it/) and hosted on our services. For the most complex and loaded systems, our partner and Keycloak experts behind this add-on, can provide assistance: [contact us](mailto:sales@clever-cloud.com). ## How it works? + When you create the Keycloak add-on, Clever Cloud automatically deploys: - A [Java](/doc/applications/java/java-jar) instance with Keycloak pre-loaded and configured @@ -56,6 +59,7 @@ By default, Keycloak on Clever Cloud uses small-size resources, i.e: - Less than 100 MB in FS Bucket They are dimensioned to suit a majority of needs. Even if this Keycloak add-on might handle heavy traffic and an important number of simultaneous connections, the default configuration should handle the following load (based on [Keycloak](https://www.keycloak.org/high-availability/concepts-memory-and-cpu-sizing) sizing recommendation): + - 5 logins by second - 90 credential grants by the second - 70 refresh tokens by second @@ -65,6 +69,7 @@ You can however manage and adjust them directly in the Console to fit your needs ## Create a Keycloak add-on ### From the Console + 1. [Create a new add-on](https://console.clever-cloud.com/users/me/addons/new) by clicking the **Create…** dropdown in the sidebar and then **an add-on** 2. Select the Keycloak add-on 3. You can skip linking the add-on to an application, it won't be needed @@ -77,7 +82,7 @@ You can however manage and adjust them directly in the Console to fit your needs Make sure you have `clever-tools` installed locally. Please refer to the [setup guide](/doc/cli/install/) if needed. In your terminal, run `clever addon create keycloak --org ` (`--org` is optional). You'll get URLs to manage your Keycloak instance and the temporary credentials: -``` +```bash $ clever addon create keycloak myKeycloak Add-on created successfully! ID: addon_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx @@ -117,6 +122,7 @@ If you also need a more resilient database, contact your sales representative or ## Version management To change the version of a Keycloak add-on on Clever Cloud, you can: + - Use the Console Dashboard - Use [Clever Tools](/doc/cli/operators/) - Update the `CC_KEYCLOAK_VERSION` environment variable of the underlying Java Application and rebuild it @@ -183,12 +189,12 @@ You can restrict who can reach your Keycloak instance with two complementary mec Starting with version `26.6`, the Keycloak add-on can filter incoming requests based on the client's public IP, with separate rules for each realm and for each endpoint category (admin console and admin API, public endpoints, SCIM provisioning). Filtering is configured through environment variables of the underlying Java application. Each variable accepts a comma-separated list of IP addresses and blocked requests receive an `HTTP 403` response. -| Variable | Scope | Protected paths | -|----------|-------|-----------------| -| `CC_KEYCLOAK_ADMIN_IPS_` | Admin endpoints of a given realm | `/admin//*`, `/admin/realms//*` | -| `CC_KEYCLOAK_PUBLIC_IPS_` | Public endpoints of a given realm (login, tokens, user authentication) | `/realms//*` | -| `CC_KEYCLOAK_SCIM_IPS_` | SCIM provisioning endpoints of a given realm (requires the `scim-api` feature, see below) | `/realms//scim/*` | -| `CC_KEYCLOAK_ADMIN_IPS` | Global fallback for admin endpoints not covered by a per-realm rule | `/admin/*` | +| Variable | Scope | Protected paths | +| -------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------- | +| `CC_KEYCLOAK_ADMIN_IPS_` | Admin endpoints of a given realm | `/admin//*`, `/admin/realms//*` | +| `CC_KEYCLOAK_PUBLIC_IPS_` | Public endpoints of a given realm (login, tokens, user authentication) | `/realms//*` | +| `CC_KEYCLOAK_SCIM_IPS_` | SCIM provisioning endpoints of a given realm (requires the `scim-api` feature, see below) | `/realms//scim/*` | +| `CC_KEYCLOAK_ADMIN_IPS` | Global fallback for admin endpoints not covered by a per-realm rule | `/admin/*` | The realm name in the variable suffix must match the realm name as it appears in URLs (case-sensitive). Per-realm rules take precedence over the global admin filter. If none of these variables is set, Keycloak keeps its standard public behavior. @@ -221,6 +227,7 @@ You can also assign these flows to your own clients if needed. Since version `25.06`, Keycloak add-on exposes [Prometheus](https://prometheus.io/) metrics on port `9000`. Use Clever Cloud's [Grafana integration](/doc/metrics/#publish-your-own-metrics) to visualize them. You can also use a Grafana dashboard ready to import, available starting with Keycloak `26.2` release: + - Go to the `Metrics in Grafana` section of your organisation or personal space in [Console](https://console.clever-cloud.com/) - Open Grafana, click on the `+` icon in the upper right corner and select `Import` dashboard - Import this [JSON file](https://cc-keycloak.cellar-c2.services.clever-cloud.com/keycloak-grafana-dashboard.json) diff --git a/content/doc/addons/kms.md b/content/doc/addons/kms.md index d1006d83f..c39801eb6 100644 --- a/content/doc/addons/kms.md +++ b/content/doc/addons/kms.md @@ -58,6 +58,7 @@ flowchart LR ## Prerequisites To use Clever KMS you'll need : + - An authorized access for your organisation - Clever Tools with KMS commands - A compatible Vault CLI @@ -245,6 +246,7 @@ The ciphertext isn't stored by Clever KMS; it must be stored by the client in it ### Decrypt ciphertext To get back the original payload, call the decrypt route with the ciphertext and the transit key name: + ```bash ❯ vault write transit/decrypt/my-key ciphertext=vault:v1:75Le-Zs1fzJP-_l-vPdePoabvNXYGsBtcj0Ii4LcJ_eNHeImfNWz1aAH82uyPcGgyfExcyNk5Ds9lW-C Key Value diff --git a/content/doc/addons/materia-kv.md b/content/doc/addons/materia-kv.md index 82a523fee..ab5ef2c15 100644 --- a/content/doc/addons/materia-kv.md +++ b/content/doc/addons/materia-kv.md @@ -64,7 +64,7 @@ redis-cli -h $KV_HOST -p $KV_PORT --tls PING Here is an example of what you can expect: -``` +```bash $ clever addon create kv testKV Add-on created successfully! @@ -88,11 +88,11 @@ You can also deploy Materia KV add-ons with [Terraform provider](https://registr To connect to a Materia KV add-on, you need 3 parameters: the host, the port and a token. You can set these parameters as environment variables by doing `source <(clever addon env addon ADDON_ID -F shell)`. The variables set are: -* `$KV_HOST` and its alias `$REDIS_HOST` -* `$KV_PORT` and its alias `$REDIS_PORT` -* `$KV_TOKEN` and its alias `$REDIS_PASSWORD` -* `$REDIS_CLI_URL` -* `$REDISCLI_AUTH` +- `$KV_HOST` and its alias `$REDIS_HOST` +- `$KV_PORT` and its alias `$REDIS_PORT` +- `$KV_TOKEN` and its alias `$REDIS_PASSWORD` +- `$REDIS_CLI_URL` +- `$REDISCLI_AUTH` You can directly use these environment variables to connect to a Materia KV add-on using `redis-cli` if `REDISCLI_AUTH` is set: @@ -102,7 +102,6 @@ redis-cli -h $KV_HOST -p $KV_PORT --tls Materia KV is also compatible with alternatives such as [iredis](https://github.com/laixintao/iredis). - ### Fish shell users If you use the Fish shell, you can use the following command to set the environment variables: @@ -119,7 +118,7 @@ By default, Materia KV uses TLS on the 6379 port. You can use non-TLS connection We're exploring how [Clever Tools](https://github.com/CleverCloud/clever-tools/) can natively support Materia KV and helps you to manage such add-ons without any additional software or configuration. The `clever kv` command is available since [version 3.11](https://github.com/CleverCloud/clever-tools/releases/tag/3.11.0). -* [Learn more about Clever KV](/doc/cli/kv-stores/) +- [Learn more about Clever KV](/doc/cli/kv-stores/) ### Supported types and commands @@ -131,85 +130,86 @@ Supported value types are: Find below the list of currently supported commands: -|
Commands
| Description | -| ------- | ----------- | -| `APPEND` | If `key` already exists and is a string, this command appends the value at the end of the string. If `key` doesn't exist it is created and set as an empty string, so `APPEND` will be similar to `SET` in this special case. | -| `AUTH` | Authenticate the current connection using the token as `password`. | -| `CLIENT ID` | Returns the `ID` of the current connection. A connection ID has is never repeated and is monotonically incremental. | -| `COMMAND` | Return an array with details about every supported command. | -| `COMMAND COUNT` | Return the number of supported commands. | -| `COMMAND DOCS` | Return documentary information about commands. By default, the reply includes all the server's commands. You can use the optional command-name argument to specify the names of one or more commands. The reply includes a map for each returned command. | -| `COMMAND INFO` | Returns an array reply of details about multiple Materia KV commands. Same result format as `COMMAND` except you can specify which commands get returned. If you request details about non-existing commands, their return position will be `nil`. | -| `COMMAND LIST` | Return an array of the server's command names. | -| `DBSIZE` | Return the number of keys in the currently-selected database. | -| `DECR` | Decrements the number stored at `key` by one. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | -| `DECRBY` | Decrements the number stored at `key` by the given `decrement`. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | -| `DEL` | Removes the specified `key`. A key is ignored if it doesn't exist. | -| `EXISTS` | Returns if `key` exists. | -| `EXPIRE` | Set a `key` time to live in seconds. After the timeout has expired, the `key` will be automatically deleted. The time to live can be updated using the `EXPIRE` command or cleared using the `PERSIST` command. | -| `EXPIREAT` | Sets a `key` to expire at the specified Unix timestamp (in seconds). After that time, the `key` is automatically deleted. Returns `1` if the timeout was set, `0` if the `key` doesn't exist. | -| `FLUSHALL` | Delete all the keys of all the existing databases, not just the currently selected one. This command never fails. | -| `FLUSHDB` | Delete all the keys of the currently selected DB. This command never fails. | -| `GET` | Get the value of `key`. If the `key` doesn't exist the special value nil is returned. An error is returned if the value stored at `key` is not a string, because `GET` only handles string values. | -| `GETBIT` | Returns the bit value at offset in the string value stored at `key`. | -| `GETDEL` | Gets the value of `key` and deletes the key. If the `key` doesn't exist, returns `nil`. Returns an error if the value stored at `key` isn't a string. | -| `GETRANGE` | Returns the substring of the string value stored at `key`, determined by the offsets start and end (both are inclusive). Negative offsets can be used in order to provide an offset starting from the end of the string. So `-1` means the last character, `-2` the penultimate and so forth. | -| `HDEL` | Removes the specified fields from the hash stored at `key`. Specified fields that do not exist within this hash are ignored. If `key` does not exist, it is treated as an empty hash and this command returns `0`. | -| `HELLO` | Switch to a different protocol, optionally authenticating and setting the connection's name, or provide a contextual client report. It always replies with a list of current server and connection properties. | -| `HEXISTS` | Returns `1` if `field` exists in the hash stored at `key`, `0` if `field` or `key` don't exist. Returns an error if the value stored at `key` isn't a hash. | -| `HGET` | Returns the value associated with `field` in the hash stored at `key`. If `key` does not exist, or `field` is not present in the hash, `nil` is returned. | -| `HGETALL` | Returns all fields and values of the hash stored at `key`. In the returned value, every field name is followed by its value, so the length of the reply is twice the size of the hash. | -| `HINCRBY` | Increments the number stored at `field` in the hash stored at `key` by the given `increment`. If `key` doesn't exist, creates a new key holding a hash. If `field` doesn't exist, sets the value to `0` before performing the operation. Returns an error if the field contains a value of the wrong type or the resulting value exceeds a 64-bit signed integer. | -| `HLEN` | Returns the number of fields contained in the hash stored at `key`. If `key` does not exist, it is treated as an empty hash and `0` is returned. | -| `HMGET` | Returns the values associated with the specified `fields` in the hash stored at `key`. For every field that does not exist in the hash, a `nil` value is returned. Because of this, the operation never fails. | -| `HSCAN` | Incrementally iterate over hash fields and associated values. It is a cursor based iterator, this means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call. An iteration starts when the cursor is set to `0`, and terminates when the cursor returned by the server is `0`. | -| `HSET` | Sets the specified fields to their respective values in the hash stored at `key`. If `key` does not exist, a new key holding a hash is created. If `key` exists but does not hold a hash, an error is returned. | -| `HSETNX` | Sets `field` in the hash stored at `key` to `value`, only if `field` doesn't yet exist. If `key` doesn't exist, creates a new key holding a hash. If `field` already exists, the operation has no effect. Returns `1` if `field` is a new field in the hash and the value was set, `0` if `field` already exists. | -| `INCR` | Increments the number stored at `key` by one. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | -| `INCRBY` | Increments the number stored at `key` by the given `increment`. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | -| `INCRBYFLOAT` | Increment the string representing a floating point number stored at `key` by the specified `increment`. If the key does not exist, it is set to `0` before performing the operation. An error is returned if the key contains a value of the wrong type or a string that can not be represented as a floating point number. | -| `INFO` | The `INFO` command returns information and statistics about the server in a format that is simple to parse by computers and easy to read by humans. | -| `JSON.DEL` | Deletes JSON value at path from key. Returns the number of paths deleted. Can delete array elements or object fields. | -| `JSON.GET` | Gets JSON value at path from key. Supports both single and multiple path queries with different path notations. | -| `JSON.SET` | Sets JSON value at root path (`$`) and updating existing paths in key. Creates new key if it doesn't exist. | -| `KEYS` | Returns all keys matching `pattern`, can be `*` | -| `LOLWUT` | Returns Materia KV's version and might be hiding an easter egg 👀 | -| `MGET` | Returns the values of all specified keys. For every key that doesn't hold a string value or doesn't exist, the special value `nil` is returned. Because of this, the operation never fails. | -| `MSET` | Sets the given keys to their respective values. `MSET` replaces existing values with new values, just as regular `SET`. `MSET` is atomic, so all given keys are set at once. It is not possible for clients to see that some keys were updated while others are unchanged. | -| `PERSIST` | Remove the existing time to live associated with the `key`. | -| `PEXPIRE` | Set a `key` time to live in milliseconds. After the timeout has expired, the `key` will be automatically deleted. The time to live can be updated using the `PEXPIRE` command or cleared using the `PERSIST` command. | -| `PEXPIREAT` | Sets a `key` to expire at the specified absolute Unix timestamp in milliseconds. After that time, the `key` is automatically deleted. Returns `1` if the timeout was set, `0` if the `key` doesn't exist. | -| `PING` | Returns `PONG` if no argument is provided, otherwise return a copy of the argument as a bulk. | -| `PTTL` | Returns the remaining time to live of a `key`, in milliseconds. | -| `SADD` | Add the specified members to the set stored at `key`. Specified members that are already a member of this set are ignored. If `key` doesn't exist, a new set is created before adding the specified members. | -| `SCAN` | Incrementally iterate over a collection of elements. It is a cursor based iterator, this means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call. An iteration starts when the cursor is set to `0`, and terminates when the cursor returned by the server is `0`. | -| `SCARD` | Returns the set cardinality (number of elements) of the set stored at `key`. | -| `SDIFF` | Returns the members of the set resulting from the difference between the first set and all the successive sets. | -| `SDIFFSTORE` | This command is equal to `SDIFF`, but instead of returning the resulting set, it is stored in `destination`. If `destination` already exists, it is overwritten. | -| `SET` | Set `key` to hold the string `value`. If key already holds a value, it is overwritten, regardless of its type. | -| `SETBIT` | Sets or clears the bit at offset in the string value stored at `key`. | -| `SINTER` | Returns the members of the set resulting from the intersection of all the given sets. | -| `SINTERCARD` | Returns the number of elements that would result from the intersection of all given sets. | -| `SINTERSTORE` | This command is equal to `SINTER`, but instead of returning the resulting set, it is stored in `destination`. If `destination` already exists, it is overwritten. | -| `SISMEMBER` | Returns if `member` is a member of the set stored at `key`. | -| `SMEMBERS` | Returns all the members of the set value stored at `key`. | -| `SMISMEMBER` | Returns whether each member is a member of the set stored at `key`. For every member, `1` is returned if the value is a member of the set, or `0` if the element is not a member of the set or if `key` doesn't exist. | -| `SMOVE` | Move `member` from the set at `source` to the set at `destination`. This operation is atomic. In every given moment the element will appear to be a member of `source` or `destination` for other clients. | -| `SPOP` | Removes and returns one or more random members from the set value stored at `key`. | -| `SRANDMEMBER` | When called with just the `key` argument, return a random element from the set value stored at `key`. | -| `SREM` | Remove the specified members from the set stored at `key`. Specified members that are not a member of this set are ignored. If `key` doesn't exist, it is treated as an empty set and this command returns `0`. | -| `SSCAN` | Incrementally iterate over set elements. It is a cursor based iterator, this means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call. An iteration starts when the cursor is set to `0`, and terminates when the cursor returned by the server is `0`. | -| `STRLEN` | Returns the length of the string value stored at `key`. An error is returned when key holds a non-string value. | -| `SUNION` | Returns the members of the set resulting from the union of all the given sets. | -| `SUNIONSTORE` | This command is equal to `SUNION`, but instead of returning the resulting set, it is stored in `destination`. If `destination` already exists, it is overwritten. | -| `TTL` | Returns the remaining time to live of a `key`, in seconds. | -| `TYPE` | Returns the string representation of the type of the value stored at `key`. Can be: `hash`, `list`, `set` or `string`. | +| Commands | Description | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `APPEND` | If `key` already exists and is a string, this command appends the value at the end of the string. If `key` doesn't exist it is created and set as an empty string, so `APPEND` will be similar to `SET` in this special case. | +| `AUTH` | Authenticate the current connection using the token as `password`. | +| `CLIENT ID` | Returns the `ID` of the current connection. A connection ID has is never repeated and is monotonically incremental. | +| `COMMAND` | Return an array with details about every supported command. | +| `COMMAND COUNT` | Return the number of supported commands. | +| `COMMAND DOCS` | Return documentary information about commands. By default, the reply includes all the server's commands. You can use the optional command-name argument to specify the names of one or more commands. The reply includes a map for each returned command. | +| `COMMAND INFO` | Returns an array reply of details about multiple Materia KV commands. Same result format as `COMMAND` except you can specify which commands get returned. If you request details about non-existing commands, their return position will be `nil`. | +| `COMMAND LIST` | Return an array of the server's command names. | +| `DBSIZE` | Return the number of keys in the currently-selected database. | +| `DECR` | Decrements the number stored at `key` by one. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | +| `DECRBY` | Decrements the number stored at `key` by the given `decrement`. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | +| `DEL` | Removes the specified `key`. A key is ignored if it doesn't exist. | +| `EXISTS` | Returns if `key` exists. | +| `EXPIRE` | Set a `key` time to live in seconds. After the timeout has expired, the `key` will be automatically deleted. The time to live can be updated using the `EXPIRE` command or cleared using the `PERSIST` command. | +| `EXPIREAT` | Sets a `key` to expire at the specified Unix timestamp (in seconds). After that time, the `key` is automatically deleted. Returns `1` if the timeout was set, `0` if the `key` doesn't exist. | +| `FLUSHALL` | Delete all the keys of all the existing databases, not just the currently selected one. This command never fails. | +| `FLUSHDB` | Delete all the keys of the currently selected DB. This command never fails. | +| `GET` | Get the value of `key`. If the `key` doesn't exist the special value nil is returned. An error is returned if the value stored at `key` is not a string, because `GET` only handles string values. | +| `GETBIT` | Returns the bit value at offset in the string value stored at `key`. | +| `GETDEL` | Gets the value of `key` and deletes the key. If the `key` doesn't exist, returns `nil`. Returns an error if the value stored at `key` isn't a string. | +| `GETRANGE` | Returns the substring of the string value stored at `key`, determined by the offsets start and end (both are inclusive). Negative offsets can be used in order to provide an offset starting from the end of the string. So `-1` means the last character, `-2` the penultimate and so forth. | +| `HDEL` | Removes the specified fields from the hash stored at `key`. Specified fields that do not exist within this hash are ignored. If `key` does not exist, it is treated as an empty hash and this command returns `0`. | +| `HELLO` | Switch to a different protocol, optionally authenticating and setting the connection's name, or provide a contextual client report. It always replies with a list of current server and connection properties. | +| `HEXISTS` | Returns `1` if `field` exists in the hash stored at `key`, `0` if `field` or `key` don't exist. Returns an error if the value stored at `key` isn't a hash. | +| `HGET` | Returns the value associated with `field` in the hash stored at `key`. If `key` does not exist, or `field` is not present in the hash, `nil` is returned. | +| `HGETALL` | Returns all fields and values of the hash stored at `key`. In the returned value, every field name is followed by its value, so the length of the reply is twice the size of the hash. | +| `HINCRBY` | Increments the number stored at `field` in the hash stored at `key` by the given `increment`. If `key` doesn't exist, creates a new key holding a hash. If `field` doesn't exist, sets the value to `0` before performing the operation. Returns an error if the field contains a value of the wrong type or the resulting value exceeds a 64-bit signed integer. | +| `HLEN` | Returns the number of fields contained in the hash stored at `key`. If `key` does not exist, it is treated as an empty hash and `0` is returned. | +| `HMGET` | Returns the values associated with the specified `fields` in the hash stored at `key`. For every field that does not exist in the hash, a `nil` value is returned. Because of this, the operation never fails. | +| `HSCAN` | Incrementally iterate over hash fields and associated values. It is a cursor based iterator, this means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call. An iteration starts when the cursor is set to `0`, and terminates when the cursor returned by the server is `0`. | +| `HSET` | Sets the specified fields to their respective values in the hash stored at `key`. If `key` does not exist, a new key holding a hash is created. If `key` exists but does not hold a hash, an error is returned. | +| `HSETNX` | Sets `field` in the hash stored at `key` to `value`, only if `field` doesn't yet exist. If `key` doesn't exist, creates a new key holding a hash. If `field` already exists, the operation has no effect. Returns `1` if `field` is a new field in the hash and the value was set, `0` if `field` already exists. | +| `INCR` | Increments the number stored at `key` by one. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | +| `INCRBY` | Increments the number stored at `key` by the given `increment`. If the `key` doesn't exist, it is set to `0` before performing the operation. An error is returned if `key` contains a value of the wrong type or contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers. | +| `INCRBYFLOAT` | Increment the string representing a floating point number stored at `key` by the specified `increment`. If the key does not exist, it is set to `0` before performing the operation. An error is returned if the key contains a value of the wrong type or a string that can not be represented as a floating point number. | +| `INFO` | The `INFO` command returns information and statistics about the server in a format that is simple to parse by computers and easy to read by humans. | +| `JSON.DEL` | Deletes JSON value at path from key. Returns the number of paths deleted. Can delete array elements or object fields. | +| `JSON.GET` | Gets JSON value at path from key. Supports both single and multiple path queries with different path notations. | +| `JSON.SET` | Sets JSON value at root path (`$`) and updating existing paths in key. Creates new key if it doesn't exist. | +| `KEYS` | Returns all keys matching `pattern`, can be `*` | +| `LOLWUT` | Returns Materia KV's version and might be hiding an easter egg 👀 | +| `MGET` | Returns the values of all specified keys. For every key that doesn't hold a string value or doesn't exist, the special value `nil` is returned. Because of this, the operation never fails. | +| `MSET` | Sets the given keys to their respective values. `MSET` replaces existing values with new values, just as regular `SET`. `MSET` is atomic, so all given keys are set at once. It is not possible for clients to see that some keys were updated while others are unchanged. | +| `PERSIST` | Remove the existing time to live associated with the `key`. | +| `PEXPIRE` | Set a `key` time to live in milliseconds. After the timeout has expired, the `key` will be automatically deleted. The time to live can be updated using the `PEXPIRE` command or cleared using the `PERSIST` command. | +| `PEXPIREAT` | Sets a `key` to expire at the specified absolute Unix timestamp in milliseconds. After that time, the `key` is automatically deleted. Returns `1` if the timeout was set, `0` if the `key` doesn't exist. | +| `PING` | Returns `PONG` if no argument is provided, otherwise return a copy of the argument as a bulk. | +| `PTTL` | Returns the remaining time to live of a `key`, in milliseconds. | +| `SADD` | Add the specified members to the set stored at `key`. Specified members that are already a member of this set are ignored. If `key` doesn't exist, a new set is created before adding the specified members. | +| `SCAN` | Incrementally iterate over a collection of elements. It is a cursor based iterator, this means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call. An iteration starts when the cursor is set to `0`, and terminates when the cursor returned by the server is `0`. | +| `SCARD` | Returns the set cardinality (number of elements) of the set stored at `key`. | +| `SDIFF` | Returns the members of the set resulting from the difference between the first set and all the successive sets. | +| `SDIFFSTORE` | This command is equal to `SDIFF`, but instead of returning the resulting set, it is stored in `destination`. If `destination` already exists, it is overwritten. | +| `SET` | Set `key` to hold the string `value`. If key already holds a value, it is overwritten, regardless of its type. | +| `SETBIT` | Sets or clears the bit at offset in the string value stored at `key`. | +| `SINTER` | Returns the members of the set resulting from the intersection of all the given sets. | +| `SINTERCARD` | Returns the number of elements that would result from the intersection of all given sets. | +| `SINTERSTORE` | This command is equal to `SINTER`, but instead of returning the resulting set, it is stored in `destination`. If `destination` already exists, it is overwritten. | +| `SISMEMBER` | Returns if `member` is a member of the set stored at `key`. | +| `SMEMBERS` | Returns all the members of the set value stored at `key`. | +| `SMISMEMBER` | Returns whether each member is a member of the set stored at `key`. For every member, `1` is returned if the value is a member of the set, or `0` if the element is not a member of the set or if `key` doesn't exist. | +| `SMOVE` | Move `member` from the set at `source` to the set at `destination`. This operation is atomic. In every given moment the element will appear to be a member of `source` or `destination` for other clients. | +| `SPOP` | Removes and returns one or more random members from the set value stored at `key`. | +| `SRANDMEMBER` | When called with just the `key` argument, return a random element from the set value stored at `key`. | +| `SREM` | Remove the specified members from the set stored at `key`. Specified members that are not a member of this set are ignored. If `key` doesn't exist, it is treated as an empty set and this command returns `0`. | +| `SSCAN` | Incrementally iterate over set elements. It is a cursor based iterator, this means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call. An iteration starts when the cursor is set to `0`, and terminates when the cursor returned by the server is `0`. | +| `STRLEN` | Returns the length of the string value stored at `key`. An error is returned when key holds a non-string value. | +| `SUNION` | Returns the members of the set resulting from the union of all the given sets. | +| `SUNIONSTORE` | This command is equal to `SUNION`, but instead of returning the resulting set, it is stored in `destination`. If `destination` already exists, it is overwritten. | +| `TTL` | Returns the remaining time to live of a `key`, in seconds. | +| `TYPE` | Returns the string representation of the type of the value stored at `key`. Can be: `hash`, `list`, `set` or `string`. | ### JSON commands Materia KV provides preliminary support for JSON data type operations, compatible with Redis API JSON commands and clients. Unlike Redis JSON which uses a dedicated data type, our implementation works directly with classic string data types while maintaining API compatibility. #### Path Syntax and Behavior + - `$`: Root element (required for setting values, optional for `GET`/`DEL`) - `$.field`: Access field in object - `$..field`: Recursively search for all matching fields @@ -251,6 +251,7 @@ OK ``` #### Current Limitations + - `JSON.SET` can only create new documents at root path (`$`) - `JSON.SET` can't create new fields in existing documents - Nested path creation is not supported (e.g., `$.new.child.field`) @@ -266,7 +267,7 @@ The GraphQL layer is **read-only** today — the schema exposes no mutation type Materia KV is a distributed cluster: the GraphQL endpoint is a single URL shared by every add-on in a given region, over HTTPS on the standard port. For the Paris region, it is: -``` +```text https://materiakv-graphql.eu-fr-1.services.clever-cloud.com/graphql ``` @@ -402,11 +403,11 @@ JSON documents written via `JSON.SET` are stored on top of strings. The schema h We've prepared a few examples to help you get started with Materia KV: -* [Materia KV Go client](https://github.com/CleverCloud/mkv-go-cli) -* [Materia KV raw TCP V demo](https://github.com/CleverCloud/mkv-raw-tcp-v) -* [Materia KV raw TCP Ruby demo](https://github.com/CleverCloud/mkv-raw-tcp-ruby) -* [Materia KV PHP sessions with TTL demo](https://github.com/CleverCloud/php-sessions-kv-example) -* [Materia KV write via Redis API, read via GraphQL](https://github.com/CleverCloud/kv-graphql-example) +- [Materia KV Go client](https://github.com/CleverCloud/mkv-go-cli) +- [Materia KV raw TCP V demo](https://github.com/CleverCloud/mkv-raw-tcp-v) +- [Materia KV raw TCP Ruby demo](https://github.com/CleverCloud/mkv-raw-tcp-ruby) +- [Materia KV PHP sessions with TTL demo](https://github.com/CleverCloud/php-sessions-kv-example) +- [Materia KV write via Redis API, read via GraphQL](https://github.com/CleverCloud/kv-graphql-example) ## How Materia KV differs from Redis @@ -478,14 +479,14 @@ Active background cleanup of expired keys is under active development. Future re Materia KV enforces stricter size limits than Redis due to the constraints of the underlying distributed transaction engine. -| Limit | Redis | Materia KV | -|-------|-------|------------| -| Max key size | 512 MB | 8 KB | -| Max value size | 512 MB | 5 MB | -| Max transaction size | N/A | 10 MB (reads + writes) | -| Transaction duration | N/A | 5 seconds | -| Max keys per `DEL` | No limit | 100 | -| Storage per add-on | Plan-dependent | 128 MB (during Beta, can be increased) | +| Limit | Redis | Materia KV | +| -------------------- | -------------- | -------------------------------------- | +| Max key size | 512 MB | 8 KB | +| Max value size | 512 MB | 5 MB | +| Max transaction size | N/A | 10 MB (reads + writes) | +| Transaction duration | N/A | 5 seconds | +| Max keys per `DEL` | No limit | 100 | +| Storage per add-on | Plan-dependent | 128 MB (during Beta, can be increased) | The system enforces the 8 KB key limit and 5 MB value limit at the application level. The 10 MB transaction limit and 5-second duration are FoundationDB constraints that apply to the sum of all data read and written within a single command's transaction. diff --git a/content/doc/addons/materia-ts.md b/content/doc/addons/materia-ts.md index 56159df43..f3ccd07b5 100644 --- a/content/doc/addons/materia-ts.md +++ b/content/doc/addons/materia-ts.md @@ -38,12 +38,12 @@ The add-on is immediately created and you can start using it with provided token Materia TS uses Warp10 time series engine and WarpsScript query language. It provides PromQL and Prometheus Ingestion gateway in addition to warp10 endpoints. You can also use [Time Series Language (TSL)](https://github.com/ovh/tsl/) previously written by OVHcloud Metrics team. -| Endpoint | Description | -| -------- | ----------- | +| Endpoint | Description | +| ----------------------------------------------------------------------- | ------------------------------------ | | `https://materiats.eu-fr-1.services.clever-cloud.com:443/api/v0/update` | Warp10 compatible Ingestion endpoint | -| `https://materiats.eu-fr-1.services.clever-cloud.com:443/api/v0/exec` | WarpScript query language endpoint | -| `https://catalyst-materiats.eu-fr-1.services.clever-cloud.com:443/` | Prometheus Ingestion gateway | -| `https://prometheus-materiats.eu-fr-1.services.clever-cloud.com:433` | PromQL endpoint | +| `https://materiats.eu-fr-1.services.clever-cloud.com:443/api/v0/exec` | WarpScript query language endpoint | +| `https://catalyst-materiats.eu-fr-1.services.clever-cloud.com:443/` | Prometheus Ingestion gateway | +| `https://prometheus-materiats.eu-fr-1.services.clever-cloud.com:433` | PromQL endpoint | For complete technical documentation on Warp 10 and its query language, see our [Warp 10 documentation](/doc/metrics/warp10/). diff --git a/content/doc/addons/matomo.md b/content/doc/addons/matomo.md index 32b7a6449..a30596c41 100644 --- a/content/doc/addons/matomo.md +++ b/content/doc/addons/matomo.md @@ -53,7 +53,7 @@ We've already integrated the Clever Cloud SSO, so you can login directly into yo Make sure you have `clever-tools` installed locally. Please refer to the [setup guide](/doc/cli/install/) if needed. In your terminal, `clever addon create addon-matomo --region --org ` (`--org` and `--region` are optional). You'll get URLs to manage your Matomo instance: -``` +```bash $ clever addon create addon-matomo myMatomo Add-on created successfully! ID: addon_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx diff --git a/content/doc/addons/metabase.md b/content/doc/addons/metabase.md index 860207b32..47a528d76 100644 --- a/content/doc/addons/metabase.md +++ b/content/doc/addons/metabase.md @@ -47,7 +47,7 @@ You can also save questions and **organize** them in _collections_. When opening Make sure you have `clever-tools` installed locally. Please refer to the [setup guide](/doc/cli/install/) if needed. In your terminal, run `clever addon create metabase --org ` (`--org` is optional). You'll get URLs to manage your Metabase instance and the temporary credentials: -``` +```bash $ clever addon create metabase myMetabase Add-on created successfully! ID: addon_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx @@ -73,7 +73,7 @@ Refer to the [Clever Tools documentation](/doc/cli/addons/) for more details on Once you created your add-on, open the management URL or look for `MB_SITE_URL` value in the Metabase dashboard from the Console. The first time you connect, you will be taken to a wizard to create the first user, data sources, etc. Then you can configure and use it. -* [Learn how to use Metabase](https://www.metabase.com/learn/metabase-basics/getting-started/find-data) +- [Learn how to use Metabase](https://www.metabase.com/learn/metabase-basics/getting-started/find-data) ## Password reset @@ -94,7 +94,7 @@ Its name is the same as the add-on name, followed by the add-on ID. Add a `CC_PRE_RUN_HOOK` environment variable with this value (replace `email@example.com` with your own address): -``` +```text MB_DB_CONNECTION_URI="$POSTGRESQL_ADDON_URI" java --add-opens java.base/java.nio=ALL-UNNAMED -jar metabase.jar reset-password email@example.com ``` diff --git a/content/doc/addons/mysql.md b/content/doc/addons/mysql.md index ce765a61c..142ca3d18 100644 --- a/content/doc/addons/mysql.md +++ b/content/doc/addons/mysql.md @@ -19,6 +19,7 @@ aliases: - /doc/en/mysql-hosting --- ## Overview + MySQL is an open source relational database management system (RDBMS). Clever Cloud's add-on uses the [Percona Server](https://www.percona.com/mysql/software/percona-server-for-mysql), a 100% compatible open source implementation, with performances improvements and more features. ## Supported Versions diff --git a/content/doc/addons/otoroshi.md b/content/doc/addons/otoroshi.md index 72e94a4dd..ae248e7a6 100644 --- a/content/doc/addons/otoroshi.md +++ b/content/doc/addons/otoroshi.md @@ -37,7 +37,7 @@ On Clever Cloud, it comes with pre-configured with features included such as [Co Make sure you have `clever-tools` installed locally. Please refer to the [setup guide](/doc/cli/install/) if needed. In your terminal, run `clever addon create otoroshi --org ` (`--org` is optional). You'll get URLs to manage your Otoroshi with LLM instance and the temporary credentials: -``` +```bash $ clever addon create otoroshi myOtoroshi Add-on created successfully! ID: addon_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx @@ -107,7 +107,7 @@ clever otoroshi open webui myOtoroshi The first time you connect, change the initial password (Security -> Administrators -> Edit user). -* [Learn how to use Otoroshi](https://maif.github.io/otoroshi/manual/docs/index.html) +- [Learn how to use Otoroshi](https://maif.github.io/otoroshi/manual/docs/index.html) ## Underlying resources @@ -130,14 +130,17 @@ They are dimensioned to suit a majority of needs. You can however manage and adj Otoroshi on Clever Cloud comes with LLM Extension. It provides a unified gateway for managing and interacting with Large Language Models through an OpenAI-compatible API interface, with [MCP support](https://www.clever.cloud/blog/company/2025/01/21/create-your-own-mcp-client-server-as-easy-as-1-2-3-with-otoroshi/). This extension streamlines the integration and management of multiple LLM providers including Ollama instances, OpenAI, Mistral, Anthropic, DeepSeek, Eleven Labs, Gemini, Groq, Hive, Hugging Face, Leonardo AI, Luma, OVH and Scaleway AI Endpoints. It supports audio, image, moderation and text generation models. ### Smarter AI operations + The extension enhances your LLM operations with intelligent workload distribution and automatic failover mechanisms, ensuring consistent service availability. It implements sophisticated features such as semantic caching to optimize response times and reduce costs, while providing comprehensive quota management capabilities for effective resource allocation. {{< youtube id="M8sA9xuE3gs">}} ### Security and Management + Security is paramount with built-in features for API key management through Otoroshi's secure vault system. The extension leverages Otoroshi's advanced authorization framework to implement fine-grained access controls based on user identity, API keys, consumer metadata, and request parameters. Additionally, prompt fencing technology helps prevent sensitive information leakage and ensures response quality. ### Observability and Enhancement + Every LLM interaction is thoroughly audited, capturing detailed information about consumers, providers, and usage patterns. The extension also includes robust prompt engineering capabilities, allowing you to create and maintain a library of contextually enhanced prompts and templates for improved efficiency and consistency in LLM interactions. - [Otoroshi LLM extension documentation](https://cloud-apim.github.io/otoroshi-llm-extension/docs/overview) @@ -148,11 +151,13 @@ Every LLM interaction is thoroughly audited, capturing detailed information abou Otoroshi on Clever Cloud integrates Coraza, a high-performance Web Application Firewall (WAF), through a WebAssembly-based plugin. This integration provides enterprise-grade security features to protect your applications using the OWASP Core Rule Set (CRS). ### Security features + The Coraza WAF plugin enables robust security policy enforcement while maintaining high performance across deployments of any scale. It seamlessly integrates with Otoroshi's existing infrastructure to provide comprehensive protection against web application threats. The integration provides advanced security capabilities through OWASP CRS implementation, allowing you to create and enforce custom security policies. The system generates detailed audit logs for security monitoring and compliance purposes, with full integration into Otoroshi's existing logging and monitoring infrastructure. ### Enterprise Capabilities + Designed for production environments, the Coraza WAF plugin offers flexible configuration options, supporting both detection and prevention modes. It enables customized rule sets per domain and provides detailed security event tracking through Otoroshi's event management system. The implementation is optimized for minimal performance impact while maintaining robust security controls. - [Otoroshi Coraza WAF documentation](https://maif.github.io/otoroshi/manual/docs/tutorials/instantiate-waf-coraza) @@ -163,6 +168,6 @@ Otoroshi exposes a comprehensive REST API that enables programmatic control over An OpenAPI descriptor is available from your instance: -``` +```text https://xxxxxxxxxxxx-api-otoroshi.services/apis/openapi.json ``` diff --git a/content/doc/addons/postgresql.md b/content/doc/addons/postgresql.md index 7407861b8..631ec2d96 100644 --- a/content/doc/addons/postgresql.md +++ b/content/doc/addons/postgresql.md @@ -38,6 +38,7 @@ It's not installed by default on a PostgreSQL add-on, if you need it, you can co ## Migrating from an old database Some applications require a non-empty database to run properly. If you want to import your **SQL** dump, you can use several methods: + 1. [WebGUI (Adminer)](https://dbms-adminer.clever-cloud.com/) 2. Command line tool for PostgreSQL administration like `psql` 3. Any PostgreSQL client such as [pgAdmin](https://www.pgadmin.org/) @@ -49,11 +50,12 @@ Some applications require a non-empty database to run properly. If you want to i You can add up to 2 replicas to an existing PostgreSQL (v12 or higher) database on Clever Cloud to enhance performance and reliability. They're set up as read-only [standby servers](https://www.postgresql.org/docs/current/runtime-config-replication.html#RUNTIME-CONFIG-REPLICATION-STANDBY) with full physical replication, in a different availability zone (AZ) or region if requested. You can see if a database is a [primary server](https://www.postgresql.org/docs/current/runtime-config-replication.html#RUNTIME-CONFIG-REPLICATION-PRIMARY) or a replica from PostgreSQL dashboard in [the Console](https://console.clever-cloud.com). If a primary server isn't available, you can promote a replica as a standalone server and link it to applications. This database will: + - Become a primary server (using `pg_ctl promote` command internally) with read/write capabilities - Delete its replication link to the original primary server > [!Note] Replication setup isn't yet available from API or the Console -> To create and configure PostgreSQL replicas, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). +> To create and configure PostgreSQL replicas, contact your sales representative or [Clever Cloud support](https://console.clever-cloud.com/ticket-center-choice). ## Direct access @@ -87,72 +89,72 @@ You can learn more about Pgpool-II on the [dedicated documentation page](/guides PostgreSQL databases managed by Clever Cloud comes with these extensions: -Extension              | Description ------------------------ | ----------- - adminpack              | Administrative functions for PostgreSQL (not supported on PostgreSQL 17+) - autoinc                | Functions for autoincrementing fields - btree_gin              | Support for indexing common datatypes in GIN - btree_gist             | Support for indexing common datatypes in GiST - citext                 | Data type for case-insensitive character strings - cube                   | Data type for multidimensional cubes - dblink                 | Connect to other PostgreSQL databases from within a database - dict_int               | Text search dictionary template for integers - dict_xsyn              | Text search dictionary template for extended synonym processing - earthdistance          | Calculate great-circle distances on the surface of the Earth - file_fdw               | Foreign-data wrapper for flat file access - fuzzystrmatch          | Determine similarities and distance between strings - hstore                 | Data type for storing sets of (key, value) pairs - hypopg                 | Hypothetical indexes for PostgreSQL - insert_username        | Functions for tracking who changed a table - intagg                 | Integer aggregator and enumerator (obsolete) - intarray               | Functions, operators, and index support for 1-D arrays of integers - isn                    | Data types for international product numbering standards - lo                     | Large Object maintenance - ltree                  | Data type for hierarchical tree-like structures - moddatetime            | Functions for tracking last modification time - pageinspect            | Inspect the contents of database pages at a low level - pg_buffercache         | Examine the shared buffer cache - pg_freespacemap        | Examine the free space map (FSM) - pg_stat_statements     | Track planning and execution statistics of all SQL statements executed - pg_trgm                | Text similarity measurement and index searching based on trigrams - pgcrypto               | Cryptographic functions - pgrowlocks             | Show row-level locking information - pgstattuple            | Show tuple-level statistics - pgvector | Vector data type and ivfflat and hnsw access methods - plcoffee               | PL/CoffeeScript (v8) trusted procedural language (not supported on PostgreSQL 14+) - plls                   | PL/LiveScript (v8) trusted procedural language (not supported on PostgreSQL 14+) - plpgsql                | PL/pgSQL procedural language - plv8                   | PL/JavaScript (v8) trusted procedural language (not supported on PostgreSQL 16+) - postgis                | PostGIS geometry and geography spatial types and functions - postgis_raster         | PostGIS raster types and functions - postgis_tiger_geocoder | PostGIS tiger geocoder and reverse geocoder - postgis_topology       | PostGIS topology spatial types and functions - postgres_fdw           | Foreign-data wrapper for remote PostgreSQL servers - refint                 | Functions for implementing referential integrity (obsolete) - seg                    | Data type for representing line segments or floating-point intervals - sslinfo                | Information about SSL certificates - tablefunc              | Functions that manipulate whole tables, including crosstab - tcn                    | Triggered change notifications - unaccent               | Text search dictionary that removes accents - uuid-ossp              | Generate universally unique identifiers (UUIDs) - xml2                   | XPath querying and XSLT +| Extension | Description | +| ---------------------- | ---------------------------------------------------------------------------------- | +| adminpack | Administrative functions for PostgreSQL (not supported on PostgreSQL 17+) | +| autoinc | Functions for autoincrementing fields | +| btree_gin | Support for indexing common datatypes in GIN | +| btree_gist | Support for indexing common datatypes in GiST | +| citext | Data type for case-insensitive character strings | +| cube | Data type for multidimensional cubes | +| dblink | Connect to other PostgreSQL databases from within a database | +| dict_int | Text search dictionary template for integers | +| dict_xsyn | Text search dictionary template for extended synonym processing | +| earthdistance | Calculate great-circle distances on the surface of the Earth | +| file_fdw | Foreign-data wrapper for flat file access | +| fuzzystrmatch | Determine similarities and distance between strings | +| hstore | Data type for storing sets of (key, value) pairs | +| hypopg | Hypothetical indexes for PostgreSQL | +| insert_username | Functions for tracking who changed a table | +| intagg | Integer aggregator and enumerator (obsolete) | +| intarray | Functions, operators, and index support for 1-D arrays of integers | +| isn | Data types for international product numbering standards | +| lo | Large Object maintenance | +| ltree | Data type for hierarchical tree-like structures | +| moddatetime | Functions for tracking last modification time | +| pageinspect | Inspect the contents of database pages at a low level | +| pg_buffercache | Examine the shared buffer cache | +| pg_freespacemap | Examine the free space map (FSM) | +| pg_stat_statements | Track planning and execution statistics of all SQL statements executed | +| pg_trgm | Text similarity measurement and index searching based on trigrams | +| pgcrypto | Cryptographic functions | +| pgrowlocks | Show row-level locking information | +| pgstattuple | Show tuple-level statistics | +| pgvector | Vector data type and ivfflat and hnsw access methods | +| plcoffee | PL/CoffeeScript (v8) trusted procedural language (not supported on PostgreSQL 14+) | +| plls | PL/LiveScript (v8) trusted procedural language (not supported on PostgreSQL 14+) | +| plpgsql | PL/pgSQL procedural language | +| plv8 | PL/JavaScript (v8) trusted procedural language (not supported on PostgreSQL 16+) | +| postgis | PostGIS geometry and geography spatial types and functions | +| postgis_raster | PostGIS raster types and functions | +| postgis_tiger_geocoder | PostGIS tiger geocoder and reverse geocoder | +| postgis_topology | PostGIS topology spatial types and functions | +| postgres_fdw | Foreign-data wrapper for remote PostgreSQL servers | +| refint | Functions for implementing referential integrity (obsolete) | +| seg | Data type for representing line segments or floating-point intervals | +| sslinfo | Information about SSL certificates | +| tablefunc | Functions that manipulate whole tables, including crosstab | +| tcn | Triggered change notifications | +| unaccent | Text search dictionary that removes accents | +| uuid-ossp | Generate universally unique identifiers (UUIDs) | +| xml2 | XPath querying and XSLT | ## On-demand extensions In the [Console's Ticket Center](https://console.clever-cloud.com/ticket-center-choice), you can ask our support team to add any of these extensions for you: -Extension | Description ------------ | ----------- -pg_cron | Job scheduler for PostgreSQL -pg_ivm | Incremental view maintenance for PostgreSQL -pg_net | Enables asynchronous (non-blocking) HTTP/HTTPS requests with SQL -pg_partman | Extension to manage partitioned tables by time or ID -pg_repack | Reorganize tables in PostgreSQL databases with minimal locks -pgaudit | Provides detailed session and/or object audit logging via the standard PostgreSQL logging facility -pgsql-http | HTTP client for PostgreSQL -pgtap | Unit testing for PostgreSQL -rum | Provides an access method to work with a RUM index -timescaledb | Enables scalable inserts and complex queries for time-series data (Apache 2 Edition) +| Extension | Description | +| ----------- | -------------------------------------------------------------------------------------------------- | +| pg_cron | Job scheduler for PostgreSQL | +| pg_ivm | Incremental view maintenance for PostgreSQL | +| pg_net | Enables asynchronous (non-blocking) HTTP/HTTPS requests with SQL | +| pg_partman | Extension to manage partitioned tables by time or ID | +| pg_repack | Reorganize tables in PostgreSQL databases with minimal locks | +| pgaudit | Provides detailed session and/or object audit logging via the standard PostgreSQL logging facility | +| pgsql-http | HTTP client for PostgreSQL | +| pgtap | Unit testing for PostgreSQL | +| rum | Provides an access method to work with a RUM index | +| timescaledb | Enables scalable inserts and complex queries for time-series data (Apache 2 Edition) | {{< callout type="warning" >}} On-demand extensions aren't available for DEV plans. diff --git a/content/doc/administrate/activity.md b/content/doc/administrate/activity.md index ad48fa162..7e16e3ae4 100644 --- a/content/doc/administrate/activity.md +++ b/content/doc/administrate/activity.md @@ -14,10 +14,10 @@ keywords: Follow your application activities and lifecycle from the **Activity** option in the application menu. Every time your application redeploys, a new block appears on the timeline. Re-deployment triggers can be either: - - An action performed by you or by a member of your organisation - - An action performed by Clever Cloud +- An action performed by you or by a member of your organisation +- An action performed by Clever Cloud - ## Re-deployment triggered by you +## Re-deployment triggered by you This happens when you or a member of your organisation performs an action on the application, like: diff --git a/content/doc/administrate/clamav.md b/content/doc/administrate/clamav.md index e00cb86bc..81250e1fc 100644 --- a/content/doc/administrate/clamav.md +++ b/content/doc/administrate/clamav.md @@ -28,8 +28,8 @@ CC_CLAMAV=true This variable is used to start these two services: -* **clamav-daemon.service** (multi-threaded daemon that uses libclamav to scan files for viruses). -* **clamav-freshclam.service** (used to update signature database sets to the latest version). +- **clamav-daemon.service** (multi-threaded daemon that uses libclamav to scan files for viruses). +- **clamav-freshclam.service** (used to update signature database sets to the latest version). The signature database is updated every 2 hours. diff --git a/content/doc/administrate/domain-names.md b/content/doc/administrate/domain-names.md index 16ddcb39f..b10056ae3 100644 --- a/content/doc/administrate/domain-names.md +++ b/content/doc/administrate/domain-names.md @@ -16,8 +16,8 @@ aliases: When creating an application, you have two (non-exclusive) choices for domain names: -* Using a personal domain name -* Using a `cleverapps.io` free domain for development and testing purposes, with built-in SSL +- Using a personal domain name +- Using a `cleverapps.io` free domain for development and testing purposes, with built-in SSL Add it in the application configuration: in the console, click **application name** in the first panel, then choose **domain names**. You'll have to choose to add a custom domain name or use a subdomain under `*.cleverapps.io`. @@ -32,6 +32,7 @@ You can set multiple custom or `cleverapps.io` domain names to an application, b ![Primary domain link](/images/primary-domain-link.webp) To select the primary domain name: + - Click the star icon next to the domain name in the **Domain names** section of your application in the Console - Use the `clever domain favourite set example.com` command in [Clever Tools](/doc/cli/applications/configuration/#domain) - Use the dedicated `/vhosts/favourite` endpoint [in the API](/api/v2/#put-/organisations/-id-/applications/-appId-/vhosts/favourite) @@ -67,90 +68,90 @@ We also support wildcard personal domain names, to do so use the standard patter Provide the following to your registrar: -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.par.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 91.208.207.214`
`@ 10800 IN A 91.208.207.215`
`@ 10800 IN A 91.208.207.216`
`@ 10800 IN A 91.208.207.217`
`@ 10800 IN A 91.208.207.218`
`@ 10800 IN A 91.208.207.220`
`@ 10800 IN A 91.208.207.221`
`@ 10800 IN A 91.208.207.222`
`@ 10800 IN A 91.208.207.223` | +| Record Type | Value | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.par.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 91.208.207.214`
`@ 10800 IN A 91.208.207.215`
`@ 10800 IN A 91.208.207.216`
`@ 10800 IN A 91.208.207.217`
`@ 10800 IN A 91.208.207.218`
`@ 10800 IN A 91.208.207.220`
`@ 10800 IN A 91.208.207.221`
`@ 10800 IN A 91.208.207.222`
`@ 10800 IN A 91.208.207.223` | ### Europe/Paris HDS (PARHDS) Provide the following to your registrar: -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.parhds.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 91.208.207.214`
`@ 10800 IN A 91.208.207.215`
`@ 10800 IN A 91.208.207.220`
`@ 10800 IN A 91.208.207.221`
`@ 10800 IN A 91.208.207.222`
`@ 10800 IN A 91.208.207.223` | +| Record Type | Value | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.parhds.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 91.208.207.214`
`@ 10800 IN A 91.208.207.215`
`@ 10800 IN A 91.208.207.220`
`@ 10800 IN A 91.208.207.221`
`@ 10800 IN A 91.208.207.222`
`@ 10800 IN A 91.208.207.223` | To benefit from certified hosting for health data, you need to deploy in an HDS zone and to sign up to a specific contract. This begins with [an initial discussion with our team](https://www.clever.cloud/fr/hebergement-donnees-de-sante/contact-hds/). ### Europe/Paris onto Scaleway (SCW) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.scw.clever-cloud.com.` | +| Record Type | Value | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.scw.clever-cloud.com.` | | A
Only if CNAME is not available | `@ 10800 IN A 212.129.27.239`
`@ 10800 IN A 212.83.186.147`
`@ 10800 IN A 212.83.186.216`
`@ 10800 IN A 212.129.27.183` | ### Europe/Gravelines HDS (GRAHDS) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.grahds.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 188.165.58.196`
`@ 10800 IN A 188.165.58.200` | +| Record Type | Value | +| ----------------------------------- | ---------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.grahds.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 188.165.58.196`
`@ 10800 IN A 188.165.58.200` | To benefit from certified hosting for health data, you need to deploy in an HDS zone and to sign up to a specific contract. This begins with [an initial discussion with our team](https://www.clever.cloud/fr/hebergement-donnees-de-sante/contact-hds/). ### Europe/London (LDN) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.ldn.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 77.68.78.36`
`@ 10800 IN A 77.68.94.247` | +| Record Type | Value | +| ----------------------------------- | ------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.ldn.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 77.68.78.36`
`@ 10800 IN A 77.68.94.247` | ### Europe/Roubaix (RBX) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.rbx.clever-cloud.com.` | +| Record Type | Value | +| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.rbx.clever-cloud.com.` | | A
Only if CNAME is not available | `@ 10800 IN A 87.98.177.176`
`@ 10800 IN A 87.98.177.181`
`@ 10800 IN A 87.98.180.173`
`@ 10800 IN A 87.98.182.136` | ### Europe/Roubaix HDS (RBXHDS) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.rbxhds.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 135.125.16.47`
`@ 10800 IN A 135.125.16.49` | +| Record Type | Value | +| ----------------------------------- | ---------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.rbxhds.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 135.125.16.47`
`@ 10800 IN A 135.125.16.49` | To benefit from certified hosting for health data, you need to deploy in an HDS zone and to sign up to a specific contract. This begins with [an initial discussion with our team](https://www.clever.cloud/fr/hebergement-donnees-de-sante/contact-hds/). ### Europe/Warsaw (WSW) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.wsw.clever-cloud.com.` | +| Record Type | Value | +| ----------------------------------- | -------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.wsw.clever-cloud.com.` | | A
Only if CNAME is not available | `@ 10800 IN A 145.239.17.127`
`@ 10800 IN A 145.239.17.192` | ### North-America/Montreal (MTL) Provide the following to your registrar: -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.mtl.clever-cloud.com.` | +| Record Type | Value | +| ----------------------------------- | -------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.mtl.clever-cloud.com.` | | A
Only if CNAME is not available | `@ 10800 IN A 158.69.109.229`
`@ 10800 IN A 149.56.117.183` | ### Asia/Singapore (SGP) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.sgp.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 51.79.197.159`
`@ 10800 IN A 51.79.197.160` | +| Record Type | Value | +| ----------------------------------- | ------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.sgp.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 51.79.197.159`
`@ 10800 IN A 51.79.197.160` | ### Oceania/Sydney (SYD) -| Record Type | Value | -| ----------- | ----- | -| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.syd.clever-cloud.com.` | -| A
Only if CNAME is not available | `@ 10800 IN A 139.99.253.215`
`@ 10800 IN A 139.99.253.237` | +| Record Type | Value | +| ----------------------------------- | -------------------------------------------------------------- | +| CNAME
Recommended | `{yoursubdomain} 10800 IN CNAME domain.syd.clever-cloud.com.` | +| A
Only if CNAME is not available | `@ 10800 IN A 139.99.253.215`
`@ 10800 IN A 139.99.253.237` | {{< callout type="warning" >}} You cannot use a CNAME on a top-level domain, or on a subdomain which already has DNS records. @@ -166,11 +167,11 @@ Remember that DNS changes may take time to propagate (usually a few hours, somet ### Contextual Example -| Domain Name Use Case | CNAME config | Record A config | Web redirections | -|-----------------------------|-------------------------------------------------|-----------------------------------------------------------------|--------------------------------------------| -| `www.example.com`
`example.com` | Point `www.example.com` to `domain.par/mtl.clever-cloud.com.` | No A record needed | Redirect `example.com` to `www.example.com` | -| `www.example.com` | Point `www.example.com` to `domain.par/mtl.clever-cloud.com.` | No A record needed | No redirect needed | -| `example.com` | No CNAME record needed | Point `example.com` to the two IP addresses of the selected region | No redirect needed | +| Domain Name Use Case | CNAME config | Record A config | Web redirections | +| ------------------------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------- | +| `www.example.com`
`example.com` | Point `www.example.com` to `domain.par/mtl.clever-cloud.com.` | No A record needed | Redirect `example.com` to `www.example.com` | +| `www.example.com` | Point `www.example.com` to `domain.par/mtl.clever-cloud.com.` | No A record needed | No redirect needed | +| `example.com` | No CNAME record needed | Point `example.com` to the two IP addresses of the selected region | No redirect needed | ## Path routing diff --git a/content/doc/administrate/log-management.md b/content/doc/administrate/log-management.md index 959860278..d07d83a51 100644 --- a/content/doc/administrate/log-management.md +++ b/content/doc/administrate/log-management.md @@ -24,7 +24,7 @@ There are two text filter modes: exact match (case-sensitive) and regular expres This interface is constantly improving, used for logs and access logs, send us your feedback through our GitHub Community: -* [Give your feedback about new Logs interface](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface) +- [Give your feedback about new Logs interface](https://github.com/CleverCloud/Community/discussions/categories/new-logs-interface) {{< callout type="info">}} Logs are retained for 7 days, sometimes more for specific customers/needs. @@ -261,9 +261,9 @@ On your terminal, use the following command: Replace the following values: - - `` - - `` - - `` +- `` +- `` +- `` {{< /tab >}} @@ -275,9 +275,9 @@ On your terminal, use the following command: Replace the following values: - - `` - - `` - - `` +- `` +- `` +- `` {{< /tab >}} diff --git a/content/doc/administrate/ssh-clever-tools.md b/content/doc/administrate/ssh-clever-tools.md index f463e1f2e..da5c71f44 100644 --- a/content/doc/administrate/ssh-clever-tools.md +++ b/content/doc/administrate/ssh-clever-tools.md @@ -33,8 +33,8 @@ For security reasons, you can't connect on the instance that hosts your Docker a You need to have at least one of the following installed locally: -* the [Clever Tools CLI](/doc/cli/) -* an SSH client (e.g [putty](https://putty.org/) for windows users) +- the [Clever Tools CLI](/doc/cli/) +- an SSH client (e.g [putty](https://putty.org/) for windows users) ### Make sure you have a properly configured SSH key @@ -128,7 +128,8 @@ You can also use `journalctl` [with other options](https://www.commandlinux.com/ ## Troubleshooting A commonly encountered issue when using `clever ssh` is: -``` + +```text Error: Failed to choose instance: 'Error while running choice script: Error: This application has no instances you can ssh to' ``` @@ -136,11 +137,13 @@ First, make sure your application is running, otherwise there is no instance to If your application is up, this means you aren't allowed to access the organisation or application. This might be due to permission issue. If you're supposed to have access to the application, this is likely due to a key management issue. To fix it: -* make sure you've added your public key on your Clever Cloud profile. You can refer to our documentation to [add your SSH key on Clever Cloud](/doc/account/ssh-keys-management#add-a-public-ssh-key-on-clever-cloud) -* make sure your SSH agent is using the proper private key + +- make sure you've added your public key on your Clever Cloud profile. You can refer to our documentation to [add your SSH key on Clever Cloud](/doc/account/ssh-keys-management#add-a-public-ssh-key-on-clever-cloud) +- make sure your SSH agent is using the proper private key A useful command for debugging is: -``` + +```bash ssh -t ssh@sshgateway-clevercloud-customers.services.clever-cloud.com -v ``` diff --git a/content/doc/administrate/ssl.md b/content/doc/administrate/ssl.md index 7e406d90a..f10020753 100644 --- a/content/doc/administrate/ssl.md +++ b/content/doc/administrate/ssl.md @@ -37,12 +37,16 @@ Clever Cloud automatically generates and renews TLS certificates for your domain ### Alternative Options #### Immediate Certificate Coverage + To avoid any period without certificate coverage (e.g., during migration from another provider): + 1. [Upload your existing certificate](#uploading-my-own-certificates) first 2. Let Clever Cloud generate a new certificate automatically when your uploaded one expires #### Wildcard Certificates + Clever Cloud's automatic certificate generation doesn't support wildcard certificates. For wildcard certificates, you can: + - Generate your own Let's Encrypt certificate - Purchase a commercial certificate - [Contact support](https://console.clever-cloud.com/ticket-center-choice) for a quote on managed certificate services @@ -128,12 +132,12 @@ You should note that `.pem`` files use the unix way of terminating lines with a You `.pem` file must be correctly formatted to successfully upload your certificate. This checklist might help you troubleshoot uncesuccessful certificate submission. -| Certificate Specifications | -|------------------------| -| ✅   The private key is included | -| ✅   Keys lenght are RSA and either 2048 or 4096 | -| ✅   ECDSA is not used to create your certificate | -| ✅   The line feed (LF) characters at the end | +| Certificate Specifications | +| ------------------------------------------------------------------------------------------------------------------------------------ | +| ✅   The private key is included | +| ✅   Keys lenght are RSA and either 2048 or 4096 | +| ✅   ECDSA is not used to create your certificate | +| ✅   The line feed (LF) characters at the end | | ✅   The certificate chain include your certificate together with all the intermediary CA certificates that signed it, in that order | ## Sending Certificates Manually diff --git a/content/doc/administrate/tcp-redirections.md b/content/doc/administrate/tcp-redirections.md index 67a3e7873..7c4ae7642 100644 --- a/content/doc/administrate/tcp-redirections.md +++ b/content/doc/administrate/tcp-redirections.md @@ -106,4 +106,4 @@ Example: if your redirection is on port **5500** and the **default** namespace, ```bash clever tcp-redirs remove 5500 --namespace default -``` \ No newline at end of file +``` diff --git a/content/doc/applications/docker.md b/content/doc/applications/docker.md index 22466c28b..7a1a3c1d1 100644 --- a/content/doc/applications/docker.md +++ b/content/doc/applications/docker.md @@ -36,8 +36,7 @@ Docker containers can encapsulate any payload, and will run consistently on and Clever Cloud allows you to deploy any application running inside a Docker container. This page explains how to set up your application to run it on our service. -> [!NOTE] -> Clever Cloud supports many languages, but some users have specific application needs. With Docker, they can create custom stacks without relying on Clever Cloud's specific support. +Clever Cloud supports many languages, but some users have specific application needs. With Docker, they can create custom stacks without relying on Clever Cloud's specific support. > [!WARNING] > [FS Buckets](/doc/best-practices/cloud-storage/#what-is-fs-bucket) access, Dockerfile validation, and Docker Compose functionalities are not supported. diff --git a/content/doc/applications/dotnet.md b/content/doc/applications/dotnet.md index dc7874c7c..a9b0cd404 100644 --- a/content/doc/applications/dotnet.md +++ b/content/doc/applications/dotnet.md @@ -50,9 +50,9 @@ You can change the default version by setting the `CC_DOTNET_VERSION` environmen Be sure that: -* You have pushed in **master** branch. -* You listen on port **8080**, by default each .NET application is created with the `ASPNETCORE_URLS="http://0.0.0.0:8080"` environment variable. -* You have committed the different files of your project and the corresponding project file (`.csproj`, `.fsproj` or `.vbproj`). +- You have pushed in **master** branch. +- You listen on port **8080**, by default each .NET application is created with the `ASPNETCORE_URLS="http://0.0.0.0:8080"` environment variable. +- You have committed the different files of your project and the corresponding project file (`.csproj`, `.fsproj` or `.vbproj`). Take an example with the [cc-dotnet-demo](https://github.com/CleverCloud/cc-dotnet-demo). diff --git a/content/doc/applications/frankenphp.md b/content/doc/applications/frankenphp.md index 73f5dbc4b..d98c04e83 100644 --- a/content/doc/applications/frankenphp.md +++ b/content/doc/applications/frankenphp.md @@ -32,6 +32,7 @@ To create a new FrankenPHP application, use the [Clever Cloud Console](https://c ```bash clever create --type frankenphp ``` + - [Learn more about Clever Tools](/doc/cli/) - [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) diff --git a/content/doc/applications/golang.md b/content/doc/applications/golang.md index 7560d24a5..aeb941036 100644 --- a/content/doc/applications/golang.md +++ b/content/doc/applications/golang.md @@ -93,12 +93,12 @@ Clever Cloud supports multiple ways to build and run a Go application. The build ### Environment variables -| Name | Description | -| :--- | :---------- | +| Name | Description | +| :----------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | | `CC_GO_BUILD_TOOL` | Available values: `gomod`, `gobuild`, `makefile`. Determines how to build and install your application. `goget` still exists but is deprecated. | -| `CC_GO_BINARY` | Required when using the `makefile` build tool. Path to the built binary, used to launch your application. | -| `CC_GO_PKG` | Package path passed to `go install`. Overrides auto-detection from `go.mod`. Default is `main.go` when no `go.mod` is present. | -| `CC_GO_RUNDIR` | Run the application from the specified path, relative to `$GOPATH/src/`. Deprecated. | +| `CC_GO_BINARY` | Required when using the `makefile` build tool. Path to the built binary, used to launch your application. | +| `CC_GO_PKG` | Package path passed to `go install`. Overrides auto-detection from `go.mod`. Default is `main.go` when no `go.mod` is present. | +| `CC_GO_RUNDIR` | Run the application from the specified path, relative to `$GOPATH/src/`. Deprecated. | The default `GOPATH` is `${HOME}/go_home`. The build command is `go install ` for all non-Makefile methods. If a `vendor/` directory is present, it is included in the build cache. @@ -122,12 +122,12 @@ An example of a `Makefile`, to use with `CC_GO_BINARY=bin/myApp`: BINARY=bin/myApp build: -# To install a specific Go version, you can add: -# go install golang.org/dl/gox.xx.x@latest -# ${HOME}/go_home/bin/gox.xx.x download -# Then use `${HOME}/go_home/bin/gox.xx.x` instead of `go` - echo "Build the application as ./${BINARY}" - go build -o ${BINARY} main.go +# To install a specific Go version, you can add: +# go install golang.org/dl/gox.xx.x@latest +# ${HOME}/go_home/bin/gox.xx.x download +# Then use `${HOME}/go_home/bin/gox.xx.x` instead of `go` + echo "Build the application as ./${BINARY}" + go build -o ${BINARY} main.go ``` - [A more complex project using a Go Workspace and a Makefile](https://github.com/CleverCloud/go-workspaces) diff --git a/content/doc/applications/haskell.md b/content/doc/applications/haskell.md index 3af098c04..94e9f773b 100644 --- a/content/doc/applications/haskell.md +++ b/content/doc/applications/haskell.md @@ -40,17 +40,17 @@ There are many ways to write web applications in Haskell, from raw [WAI](https:/ Be sure that: -* you have pushed in **master branch** -* you listen on **port 8080** -* you have one and only one binary target in your [`.cabal` file](#dependencies) -* your project has a `stack.yaml` file +- you have pushed in **master branch** +- you listen on **port 8080** +- you have one and only one binary target in your [`.cabal` file](#dependencies) +- your project has a `stack.yaml` file The steps ran in order are: -* `stack setup` -* `stack install --only-dependencies` -* `stack install` -* `.//my-exe` (or the contents of `CC_RUN_COMMAND`) +- `stack setup` +- `stack install --only-dependencies` +- `stack install` +- `.//my-exe` (or the contents of `CC_RUN_COMMAND`) The executable built by `stack build` (or the command you specify) must start a web server listening on `0.0.0.0:8080`. diff --git a/content/doc/applications/java/_index.md b/content/doc/applications/java/_index.md index 10e345e03..03e984ccb 100644 --- a/content/doc/applications/java/_index.md +++ b/content/doc/applications/java/_index.md @@ -36,7 +36,7 @@ Find detailed instructions according to your framework {{< /cards >}} -### More Frameworks +## More Frameworks Looking for instructions on a specific framework deployment? diff --git a/content/doc/applications/java/java-gradle.md b/content/doc/applications/java/java-gradle.md index 34e147fa5..b44358d8e 100644 --- a/content/doc/applications/java/java-gradle.md +++ b/content/doc/applications/java/java-gradle.md @@ -71,7 +71,7 @@ The full configuration can look like the following: You can use the following properties: | Usage | Field | Description | -|----------|-------------------|---------------------------------------------------------------------------| +| -------- | ----------------- | ------------------------------------------------------------------------- | | Optional | **build → type** | can be maven, gradle or ant | | Optional | **build → goal** | is the target you want to use to build your project | | Required | **deploy → goal** | the goal/target and options you want to execute to deploy/run you project | diff --git a/content/doc/applications/java/java-jar.md b/content/doc/applications/java/java-jar.md index 5dca6563c..cbcc43a12 100644 --- a/content/doc/applications/java/java-jar.md +++ b/content/doc/applications/java/java-jar.md @@ -104,7 +104,7 @@ The full configuration can look like the following: You can use the following properties: | Usage | Field | Description | -|----------|---------------------|---------------------------------------------------------------------------| +| -------- | ------------------- | ------------------------------------------------------------------------- | | Optional | **build → type** | can be `maven`, `gradle` or `ant` | | Optional | **build → goal** | is the target you want to use to build your project | | Optional | **deploy → goal** | the goal/target and options you want to execute to deploy/run you project | diff --git a/content/doc/applications/java/java-maven.md b/content/doc/applications/java/java-maven.md index c70be884c..5a72886aa 100644 --- a/content/doc/applications/java/java-maven.md +++ b/content/doc/applications/java/java-maven.md @@ -25,13 +25,13 @@ Note : like other runtimes, Java application needs to listen on `0.0.0.0:8080` Maven is essentially a project management and comprehension tool and as such provides a way to help with managing: -* Builds -* Documentation -* Reporting -* Dependencies -* SCMs -* Releases -* Distribution +- Builds +- Documentation +- Reporting +- Dependencies +- SCMs +- Releases +- Distribution {{% content "create-application" %}} @@ -97,7 +97,7 @@ The full configuration can look like the following: You can use the following properties: | Usage | Field | Description | -|----------|-------------------|---------------------------------------------------------------------------| +| -------- | ----------------- | ------------------------------------------------------------------------- | | Optional | **build → type** | can be `maven`, `gradle` or `ant` | | Optional | **build → goal** | is the target you want to use to build your project | | Required | **deploy → goal** | the goal/target and options you want to execute to deploy/run you project | diff --git a/content/doc/applications/java/java-war.md b/content/doc/applications/java/java-war.md index c6f7fe40f..069612667 100644 --- a/content/doc/applications/java/java-war.md +++ b/content/doc/applications/java/java-war.md @@ -23,9 +23,9 @@ Clever Cloud allows you to run WAR or EAR applications. You can deploy these pro In {{< tooltip title="JEE">}}JEE{{< /tooltip >}}, application modules are packaged as EAR and WAR based on their purpose. -* {{< tooltip title="WAR" >}}WAR{{< /tooltip >}}: Web modules which contains Servlet class files, JSP FIles, supporting files, GIF and HTML files are packaged as JAR file with *.war* extension. +- {{< tooltip title="WAR" >}}WAR{{< /tooltip >}}: Web modules which contains Servlet class files, JSP FIles, supporting files, GIF and HTML files are packaged as JAR file with *.war* extension. -* {{< tooltip title="EAR" >}}EAR{{< /tooltip >}}: `*.war` and `*.jar` files are packaged as JAR file with `.ear` extension and deployed into Application Server. EAR file contains configuration such as application security role mapping, EJB reference mapping and context root URL mapping of web modules. +- {{< tooltip title="EAR" >}}EAR{{< /tooltip >}}: `*.war` and `*.jar` files are packaged as JAR file with `.ear` extension and deployed into Application Server. EAR file contains configuration such as application security role mapping, EJB reference mapping and context root URL mapping of web modules. Note: like other runtimes, your Java application needs to listen on `0.0.0.0:8080`. @@ -94,12 +94,12 @@ Here are the mandatory fields: } ``` -|Usage |Field |Description| -|----------|--------|------------------------------------------------------------------------| -|Required |**container**|Name of the container to use. Should contain one of the values inside parentheses in the containers table (uppercase).| -|Required |file |Should contain the path of the war/ear file relative to your application root.| -| Optional |context |- Must start with a slash (/), can be "/"
- Defines the base path you want your app to be under. If your app has a /foobar endpoint, it will be available under the `/{my-context}/foobar` path.
- Not needed for an `ear` file.
- The default value for a war is the name of the war without the extensions: helloworld-1.0.war will be deployed under the `/helloworld-1.0` context.| -| Optional |checkMe |- This field is recommended.
- A path to GET in order to test if the application is really running.
- By default we will consider that the application is up if the container is up.
- With this option, we will try to GET `/{checkMe}` for each one of your wars and consider the app down until every single checkMe path that replies a 200.| +| Usage | Field | Description | +| -------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Required | **container** | Name of the container to use. Should contain one of the values inside parentheses in the containers table (uppercase). | +| Required | file | Should contain the path of the war/ear file relative to your application root. | +| Optional | context | - Must start with a slash (/), can be "/"
- Defines the base path you want your app to be under. If your app has a /foobar endpoint, it will be available under the `/{my-context}/foobar` path.
- Not needed for an `ear` file.
- The default value for a war is the name of the war without the extensions: helloworld-1.0.war will be deployed under the `/helloworld-1.0` context. | +| Optional | checkMe | - This field is recommended.
- A path to GET in order to test if the application is really running.
- By default we will consider that the application is up if the container is up.
- With this option, we will try to GET `/{checkMe}` for each one of your wars and consider the app down until every single checkMe path that replies a 200. | #### Let Clever Cloud build your application @@ -118,10 +118,10 @@ That you can do, by setting the "build" field object in the `war.json` file: } ``` -| Usage | Field | Description | -|---------------------------------|-------|------------------------------------------------| -|Required | **type** |- The tool you want to use to build your app.
- Can be "maven", "gradle", "sbt" or "ant" | -|Required | **goal** |- The goal you want the tool to execute.
- Basically, for maven, you want to put "package" in here. | +| Usage | Field | Description | +| -------- | -------- | ------------------------------------------------------------------------------------------------------ | +| Required | **type** | - The tool you want to use to build your app.
- Can be "maven", "gradle", "sbt" or "ant" | +| Required | **goal** | - The goal you want the tool to execute.
- Basically, for maven, you want to put "package" in here. | {{% content "new-relic" %}} @@ -129,18 +129,18 @@ That you can do, by setting the "build" field object in the `war.json` file: Here's the list of the configuration values for the "container" field in `war.json` (with **End Of Life** versions tagged as `EOL`): -| Value | Description | EOL | -|------------|----------------------------------------------------------------------------------------------|-----| -| JETTY9 | Use Jetty servlet container 9.x (see ) | EOL | -| JETTY11 | Use Jetty servlet container 11.x (see ) | EOL | -| PAYARA5 | Use Payara servlet container 5.x (see ) | EOL | -| PAYARA6 | Use Payara servlet container 6.x (see ) | EOL | -| TOMCAT8 | Use Tomcat servlet container 8.x (see ) | EOL | -| TOMCAT9 | Use Tomcat servlet container 9.x (see ) | | -| TOMCAT10 | Use Tomcat servlet container 10.x (see ) | | -| WILDFLY9 | Use Wildfly servlet container 9.x (see ) | EOL | -| WILDFLY27 | Use Wildfly servlet container 27.x (see ) | EOL | -| WILDFLY33 | Use Wildfly servlet container 33.x (see ) | EOL | +| Value | Description | EOL | +| --------- | ---------------------------------------------------------------------------------------- | --- | +| JETTY9 | Use Jetty servlet container 9.x (see ) | EOL | +| JETTY11 | Use Jetty servlet container 11.x (see ) | EOL | +| PAYARA5 | Use Payara servlet container 5.x (see ) | EOL | +| PAYARA6 | Use Payara servlet container 6.x (see ) | EOL | +| TOMCAT8 | Use Tomcat servlet container 8.x (see ) | EOL | +| TOMCAT9 | Use Tomcat servlet container 9.x (see ) | | +| TOMCAT10 | Use Tomcat servlet container 10.x (see ) | | +| WILDFLY9 | Use Wildfly servlet container 9.x (see ) | EOL | +| WILDFLY27 | Use Wildfly servlet container 27.x (see ) | EOL | +| WILDFLY33 | Use Wildfly servlet container 33.x (see ) | EOL | {{% content "java-env-injection" %}} diff --git a/content/doc/applications/linux.md b/content/doc/applications/linux.md index 36dbfec9b..2f2d6a206 100644 --- a/content/doc/applications/linux.md +++ b/content/doc/applications/linux.md @@ -30,6 +30,7 @@ To create a new Linux application, use the [Clever Cloud Console](https://consol ```bash clever create --type linux ``` + - [Learn more about Clever Tools](/doc/cli/) - [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) diff --git a/content/doc/applications/meteor.md b/content/doc/applications/meteor.md index ac056429e..f7b7448ce 100644 --- a/content/doc/applications/meteor.md +++ b/content/doc/applications/meteor.md @@ -36,9 +36,9 @@ Be sure your `.meteor` folder is in your git repository. To run your Meteor.js application you need a few mandatory environment variables: -* `MONGO_URL`: this is the mongo uri you can find in your mongodb's dashboard, it has to start with `mongodb://` -* `ROOT_URL`: this is your application's root url. It has to start with `http://` or `https://` -* `PORT`: keep the current value of 8080 +- `MONGO_URL`: this is the mongo uri you can find in your mongodb's dashboard, it has to start with `mongodb://` +- `ROOT_URL`: this is your application's root url. It has to start with `http://` or `https://` +- `PORT`: keep the current value of 8080 With Meteor 2.6.n comes MongoDB 5.n and [the new npm driver](https://guide.meteor.com/2.6-migration.html). You need to append `?directConnection=true` in your `MONGO_URL` to avoid the new mongo driver Service Discovery feature. @@ -94,7 +94,7 @@ Your application will then be built using `meteor build --server-only` and deplo Oplog tailing is supported on dedicated databases (starting from Hazelnut size). Contact the support to enable oplog. Once enabled, set the following environment variable: -* `MONGO_OPLOG_URL`: `mongodb://[…]mongodb.services.clever-cloud.com:{DB_PORT}/local?authSource={DB_NAME}` +- `MONGO_OPLOG_URL`: `mongodb://[…]mongodb.services.clever-cloud.com:{DB_PORT}/local?authSource={DB_NAME}` Since Meteor 2.6.n the mongo driver detect the `MONGO_OPLOG_URL` by itself and you should not add this to your environment variables. diff --git a/content/doc/applications/nodejs.md b/content/doc/applications/nodejs.md index 0dc281abb..698e08d05 100644 --- a/content/doc/applications/nodejs.md +++ b/content/doc/applications/nodejs.md @@ -45,8 +45,9 @@ To create a new Node.js & Bun application, use the [Clever Cloud Console](https: ```bash clever create --type node ``` -* [Learn more about Clever Tools](/doc/cli/) -* [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) + +- [Learn more about Clever Tools](/doc/cli/) +- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) ## Configure your Node.js & Bun application @@ -54,11 +55,11 @@ clever create --type node Be sure that: -* You listen on HTTP port **0.0.0.0:8080** -* You have a `package.json` file -* Your `package.json` either has a **scripts.start** or a **main** field -* The folder `/node_modules` is mentioned in your `.gitignore` file -* You enable production mode by setting the [environment variable](#setting-up-environment-variables-on-clever-cloud) `NODE_ENV=production` +- You listen on HTTP port **0.0.0.0:8080** +- You have a `package.json` file +- Your `package.json` either has a **scripts.start** or a **main** field +- The folder `/node_modules` is mentioned in your `.gitignore` file +- You enable production mode by setting the [environment variable](#setting-up-environment-variables-on-clever-cloud) `NODE_ENV=production` ### Memory management @@ -111,7 +112,7 @@ You can use additional scripts as an alternative to [Clever Cloud hooks](/doc/de If you need some modules you can easily add some with the *dependencies* field in your `package.json`. Here is an example: -```json {linenos=table} +```json {linenos=table} { "name" : { … }, "engines": { … }, @@ -132,12 +133,12 @@ If your application has private dependencies, you can add a [private SSH key](/d Clever Cloud supports any package manager compatible with Node.js ecosystem. The [environment variable](#setting-up-environment-variables-on-clever-cloud) `CC_NODE_BUILD_TOOL` allows you to define which one you want to use to install dependencies during the build phase: -* `bun`: uses [Bun](https://bun.sh) as a package manager and as a runtime -* `npm` or `npm-install`: default, uses [npm install](https://docs.npmjs.com/cli/install) -* `npm-ci`: uses [npm clean-install](https://docs.npmjs.com/cli/ci) -* `pnpm`: uses [pnpm](https://pnpm.io) -* `yarn-berry`: uses [Yarn](https://yarnpkg.com/) -* `custom`: use another package manager, defined with `CC_CUSTOM_BUILD_TOOL` +- `bun`: uses [Bun](https://bun.sh) as a package manager and as a runtime +- `npm` or `npm-install`: default, uses [npm install](https://docs.npmjs.com/cli/install) +- `npm-ci`: uses [npm clean-install](https://docs.npmjs.com/cli/ci) +- `pnpm`: uses [pnpm](https://pnpm.io) +- `yarn-berry`: uses [Yarn](https://yarnpkg.com/) +- `custom`: use another package manager, defined with `CC_CUSTOM_BUILD_TOOL` > [!NOTE] Yarn 1.x and 2.x deprecation > `yarn` and `yarn2` are still valid values but the Yarn team no longer maintains 1.x and 2.x branches. Use `yarn-berry` instead. @@ -153,7 +154,7 @@ deno = "latest" Then it will be installed during deployment. You can replace `latest` with a specific version. -* [Lume with Deno guide](/guides/lume-deno/) +- [Lume with Deno guide](/guides/lume-deno/) ### Automatic detection @@ -182,7 +183,7 @@ If you use Bun, your application is deployed with the latest available version o Bun includes a [native S3 client](https://bun.sh/docs/api/s3) that works with [Cellar](/doc/addons/cellar/#using-sdks), the Clever Cloud S3-compatible object storage service, with no external dependency required. -* [Learn more about Bun native Clever Cloud add-ons support with examples](https://github.com/CleverCloud/bun-addons-examples) +- [Learn more about Bun native Clever Cloud add-ons support with examples](https://github.com/CleverCloud/bun-addons-examples) ### pnpm and Yarn versions @@ -196,8 +197,8 @@ To load a specific version, set the `packageManager` field in your `package.json This is the default way to manage version for pnpm and Yarn when a new project is initialized. It will define the version used for dependency installation during the build phase and in run command. Yarn `1.22.22` remains the default system version included for legacy reasons. -* If `CC_NODE_BUILD_TOOL` is set to `yarn-berry`, the latest 4.x packaged version of Yarn becomes the system default -* If you use Yarn 1.x or Yarn 2.x, a deprecation warning is displayed during deployment as they're not maintained anymore +- If `CC_NODE_BUILD_TOOL` is set to `yarn-berry`, the latest 4.x packaged version of Yarn becomes the system default +- If you use Yarn 1.x or Yarn 2.x, a deprecation warning is displayed during deployment as they're not maintained anymore > [!TIP] > If you set `CC_NODE_BUILD_TOOL` to `yarn-berry` in any Clever Cloud runtime, Yarn 4.x becomes the default system version. diff --git a/content/doc/applications/php/_index.md b/content/doc/applications/php/_index.md index 7cf2bd727..10dd56e56 100644 --- a/content/doc/applications/php/_index.md +++ b/content/doc/applications/php/_index.md @@ -40,6 +40,7 @@ To create a new PHP application, use the [Clever Cloud Console](https://console. ```bash clever create --type php ``` + - [Learn more about Clever Tools](/doc/cli/) - [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) @@ -130,17 +131,17 @@ date.timezone=Europe/Paris Clever Cloud configures PHP's [`memory_limit`](https://www.php.net/manual/en/ini.core.php#ini.memory-limit) directive, which limits how much memory each PHP script can allocate. For web requests, this limit applies independently within each PHP-FPM worker; memory is not reserved when workers start. Both the default limit and the maximum value accepted through `MEMORY_LIMIT` depend on the application's available memory, which excludes memory reserved for the system: | Flavor | Default `memory_limit` | Maximum `MEMORY_LIMIT` | -|--------|-----------------------:|-----------------------:| -| Nano | 91 MiB | 358 MiB | -| XS | 161 MiB | 859 MiB | -| S | 249 MiB | 1683 MiB | -| M | 406 MiB | 3579 MiB | -| L | 658 MiB | 7523 MiB | -| XL | 1051 MiB | 15496 MiB | -| 2XL | 1377 MiB | 23503 MiB | -| 3XL | 1666 MiB | 31525 MiB | - -To change the limit, set [`MEMORY_LIMIT`](/doc/reference/reference-environment-variables#php) to a positive integer interpreted in MiB, without a unit suffix. +| ------ | ---------------------: | ---------------------: | +| Nano | 91 MiB | 358 MiB | +| XS | 161 MiB | 859 MiB | +| S | 249 MiB | 1683 MiB | +| M | 406 MiB | 3579 MiB | +| L | 658 MiB | 7523 MiB | +| XL | 1051 MiB | 15496 MiB | +| 2XL | 1377 MiB | 23503 MiB | +| 3XL | 1666 MiB | 31525 MiB | + +To change the limit, set [`MEMORY_LIMIT`](/doc/reference/reference-environment-variables#php) to a positive integer interpreted in MiB, without a unit suffix. For example, to allow each PHP script to allocate up to 128 MiB: @@ -148,7 +149,7 @@ For example, to allow each PHP script to allocate up to 128 MiB: clever env set MEMORY_LIMIT 128 ``` -If `MEMORY_LIMIT` exceeds the maximum value for the selected flavor, Clever Cloud keeps the automatically calculated limit and logs a warning during deployment. Clever Cloud also calculates the number of PHP-FPM workers automatically. +If `MEMORY_LIMIT` exceeds the maximum value for the selected flavor, Clever Cloud keeps the automatically calculated limit and logs a warning during deployment. Clever Cloud also calculates the number of PHP-FPM workers automatically. On XS and larger flavors, setting [`CC_CONFIGURATION_PM_MAX_CHILDREN`](/doc/reference/reference-environment-variables#php) can lower the calculated memory limit. On Nano, the platform always uses six workers. If you also set a valid `MEMORY_LIMIT`, it takes precedence over the adjustment based on the number of workers. diff --git a/content/doc/applications/php/sessions-emails.md b/content/doc/applications/php/sessions-emails.md index c8c2ccf4b..34f5199a7 100644 --- a/content/doc/applications/php/sessions-emails.md +++ b/content/doc/applications/php/sessions-emails.md @@ -53,12 +53,12 @@ Mailpace add-on can send emails through PHP `mail()` function. You have to turn Services like [Mailgun](https://www.mailgun.com/) or [Mailjet](https://www.mailjet.com/) provide SMTP servers. If your application has no other way but to use the `mail` function of PHP to send emails, you have to configure a SMTP server. This can be done through environment variables: -| Name | Description | Default | -|------|-------------|---------| -| `CC_MTA_SERVER_HOST` | Host of the SMTP server | | -| `CC_MTA_SERVER_PORT` | Port of the SMTP server | `465` | -| `CC_MTA_AUTH_USER` | User to authenticate to the SMTP server | | -| `CC_MTA_AUTH_PASSWORD` | Password to authenticate to the SMTP server | | -| `CC_MTA_SERVER_USE_TLS` | Enable or disable TLS | `true` | -| `CC_MTA_SERVER_STARTTLS` | Enable or disable STARTTLS | `false` | -| `CC_MTA_SERVER_AUTH_METHOD` | Enable or disable authentication | `on` | +| Name | Description | Default | +| --------------------------- | ------------------------------------------- | ------- | +| `CC_MTA_SERVER_HOST` | Host of the SMTP server | | +| `CC_MTA_SERVER_PORT` | Port of the SMTP server | `465` | +| `CC_MTA_AUTH_USER` | User to authenticate to the SMTP server | | +| `CC_MTA_AUTH_PASSWORD` | Password to authenticate to the SMTP server | | +| `CC_MTA_SERVER_USE_TLS` | Enable or disable TLS | `true` | +| `CC_MTA_SERVER_STARTTLS` | Enable or disable STARTTLS | `false` | +| `CC_MTA_SERVER_AUTH_METHOD` | Enable or disable authentication | `on` | diff --git a/content/doc/applications/python/_index.md b/content/doc/applications/python/_index.md index 6bdf7a131..a7c2b5d02 100644 --- a/content/doc/applications/python/_index.md +++ b/content/doc/applications/python/_index.md @@ -40,6 +40,7 @@ To create a new Python application, use the [Clever Cloud Console](https://conso ```bash clever create --type python ``` + - [Learn more about Clever Tools](/doc/cli/) - [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) diff --git a/content/doc/applications/python/servers.md b/content/doc/applications/python/servers.md index 695167877..6768e14f1 100644 --- a/content/doc/applications/python/servers.md +++ b/content/doc/applications/python/servers.md @@ -20,13 +20,13 @@ uWSGI, Gunicorn and Nginx settings can be configured by setting environment vari ### uWSGI -| Name | Description | Default | -|------|-------------|---------| -| `HARAKIRI` | Timeout (in seconds) after which an unresponsive process is killed | `180` | -| `WSGI_BUFFER_SIZE` | Maximal size (in bytes) for the headers of a request | `4096` | -| `WSGI_POST_BUFFERING` | Buffer size (in bytes) for uploads | `4096` | -| `WSGI_WORKERS` | Number of workers | depends on the scaler | -| `WSGI_THREADS` | Number of threads per worker | depends on the scaler | +| Name | Description | Default | +| --------------------- | ------------------------------------------------------------------ | --------------------- | +| `HARAKIRI` | Timeout (in seconds) after which an unresponsive process is killed | `180` | +| `WSGI_BUFFER_SIZE` | Maximal size (in bytes) for the headers of a request | `4096` | +| `WSGI_POST_BUFFERING` | Buffer size (in bytes) for uploads | `4096` | +| `WSGI_WORKERS` | Number of workers | depends on the scaler | +| `WSGI_THREADS` | Number of threads per worker | depends on the scaler | You can inject additional uWSGI configuration directives with `CC_UWSGI_EXTRA_CONFIG`. To disable the file wrapper, set `CC_UWSGI_DISABLE_FILE_WRAPPER` to `true`. @@ -39,19 +39,19 @@ To enable [uWSGI asynchronous](https://uwsgi-docs.readthedocs.io/en/latest/Async ### Gunicorn -| Name | Description | Default | -|------|-------------|---------| -| `CC_GUNICORN_WORKER_CLASS` | Type of worker to use. [Available workers](https://gunicorn.org/reference/settings/#worker_class) | `sync` | -| `CC_GUNICORN_TIMEOUT` | Gunicorn timeout (in seconds) | `30` | -| `CC_GUNICORN_LOGLEVEL` | Gunicorn log level | `info` | +| Name | Description | Default | +| -------------------------- | ------------------------------------------------------------------------------------------------- | ------- | +| `CC_GUNICORN_WORKER_CLASS` | Type of worker to use. [Available workers](https://gunicorn.org/reference/settings/#worker_class) | `sync` | +| `CC_GUNICORN_TIMEOUT` | Gunicorn timeout (in seconds) | `30` | +| `CC_GUNICORN_LOGLEVEL` | Gunicorn log level | `info` | ### Nginx -| Name | Description | Default | -|------|-------------|---------| -| `NGINX_READ_TIMEOUT` | Response timeout in seconds (similar to `HARAKIRI`) | `300` | -| `ENABLE_GZIP_COMPRESSION` | Enable gzip compression (`on`, `yes`, or `true`) | | -| `GZIP_TYPES` | The mime types to gzip | `text/plain text/css text/xml text/javascript application/json application/xml application/javascript image/svg+xml` | +| Name | Description | Default | +| ------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| `NGINX_READ_TIMEOUT` | Response timeout in seconds (similar to `HARAKIRI`) | `300` | +| `ENABLE_GZIP_COMPRESSION` | Enable gzip compression (`on`, `yes`, or `true`) | | +| `GZIP_TYPES` | The mime types to gzip | `text/plain text/css text/xml text/javascript application/json application/xml application/javascript image/svg+xml` | #### Basic authentication @@ -88,10 +88,10 @@ Nginx settings can be configured further in `clevercloud/http.json`. All its fie To enable Nginx to serve your static resources, you have to set two environment variables. -| Name | Description | -|------|-------------| +| Name | Description | +| ------------------- | --------------------------------------------------------------------------------------------- | | `STATIC_FILES_PATH` | Directory where your static files are stored (absolute path relative to the application root) | -| `STATIC_URL_PREFIX` | URL path under which you want to serve static files (e.g. `/public`) | +| `STATIC_URL_PREFIX` | URL path under which you want to serve static files (e.g. `/public`) | You can also use a [Filesystem Bucket](/doc/addons/fs-bucket) to store your static files. diff --git a/content/doc/applications/python/uv.md b/content/doc/applications/python/uv.md index 2933ad892..dc0f1c72a 100644 --- a/content/doc/applications/python/uv.md +++ b/content/doc/applications/python/uv.md @@ -44,12 +44,12 @@ The application starts with the command defined in `CC_PYTHON_UV_RUN_COMMAND`. I `CC_RUN_COMMAND` takes precedence over `CC_PYTHON_UV_RUN_COMMAND` if both are set. -| Name | Description | Required | Default | -| ---- | ----------- | -------- | ------- | -| `CC_PYTHON_UV_RUN_COMMAND` | Command to start the application (e.g. `uv run python app.py`) | Yes | - | -| `CC_RUN_COMMAND` | Overrides `CC_PYTHON_UV_RUN_COMMAND` if set | No | - | -| `ENVIRONMENT` | Set to `development` to include dev dependencies during build | No | `production` | -| `PORT` | Port used by your HTTP server | No | `8080` | +| Name | Description | Required | Default | +| -------------------------- | -------------------------------------------------------------- | -------- | ------------ | +| `CC_PYTHON_UV_RUN_COMMAND` | Command to start the application (e.g. `uv run python app.py`) | Yes | - | +| `CC_RUN_COMMAND` | Overrides `CC_PYTHON_UV_RUN_COMMAND` if set | No | - | +| `ENVIRONMENT` | Set to `development` to include dev dependencies during build | No | `production` | +| `PORT` | Port used by your HTTP server | No | `8080` | ## Differences with legacy Python deployment diff --git a/content/doc/applications/rust.md b/content/doc/applications/rust.md index a2d663f87..df940532f 100644 --- a/content/doc/applications/rust.md +++ b/content/doc/applications/rust.md @@ -40,10 +40,10 @@ Clever Cloud allows you to deploy Rust web applications. This page will explain Make sure that: -* you have pushed in **master** branch -* you listen on port **8080** -* you have committed `Cargo.lock` -* you have at least one binary target in `Cargo.toml` +- you have pushed in **master** branch +- you listen on port **8080** +- you have committed `Cargo.lock` +- you have at least one binary target in `Cargo.toml` The result of `cargo build --release --locked` must be an executable which starts a web server listening on `0.0.0.0:8080`. @@ -112,8 +112,8 @@ First, you need to use the `HTTPS` url as the git url for your dependency in you Then, you need to create a personal access token. It allows to not use your password: -* `Github`: -* `GitLab`: (API rights are needed) +- `Github`: +- `GitLab`: (API rights are needed) Once you have the token, we need to tell Git to use a credential store. For that, we are going to create it. @@ -131,10 +131,10 @@ If you have multiple private repositories, add them accordingly. Now, go into the environment variables page of your application and create those environment variables: -* `GIT_USERNAME`: your Github / gitlab / other username -* `GIT_PASSWORD`: your Github / gitlab / other password -* `CC_PRE_BUILD_HOOK`: clevercloud/pre-build.sh -* `CC_POST_BUILD_HOOK`: rm /home/bas/.git-credentials +- `GIT_USERNAME`: your Github / gitlab / other username +- `GIT_PASSWORD`: your Github / gitlab / other password +- `CC_PRE_BUILD_HOOK`: clevercloud/pre-build.sh +- `CC_POST_BUILD_HOOK`: rm /home/bas/.git-credentials This adds the git configuration before the build start and it cleans it after the build has been done. @@ -177,4 +177,3 @@ This loads the environment variable in your `main` function and use `.expect` to {{% content "url_healthcheck" %}} {{% content "request-flow" %}} - diff --git a/content/doc/applications/scala/play-framework-1.md b/content/doc/applications/scala/play-framework-1.md index 461989296..d6209e7e5 100644 --- a/content/doc/applications/scala/play-framework-1.md +++ b/content/doc/applications/scala/play-framework-1.md @@ -35,16 +35,16 @@ Clever Cloud supports Play! **1.2** to **1.11**. You can select the Play! versio The `PLAY1_VERSION` environment variable can contain one of the following values: -* `1.2` or `12` for **Play! 1.2** -* `1.3` or `12` for **Play! 1.3** -* `1.4` or `14` for **Play! 1.4** -* `1.5` or `15` for **Play! 1.5** -* `1.6` or `16` for **Play! 1.6** -* `1.7` or `17` for **Play! 1.7** -* `1.8` or `18` for **Play! 1.8** -* `1.9` or `19` for **Play! 1.9** -* `1.10` or `110` for **Play! 1.10** -* `1.11` or `111` for **Play! 1.11** +- `1.2` or `12` for **Play! 1.2** +- `1.3` or `12` for **Play! 1.3** +- `1.4` or `14` for **Play! 1.4** +- `1.5` or `15` for **Play! 1.5** +- `1.6` or `16` for **Play! 1.6** +- `1.7` or `17` for **Play! 1.7** +- `1.8` or `18` for **Play! 1.8** +- `1.9` or `19` for **Play! 1.9** +- `1.10` or `110` for **Play! 1.10** +- `1.11` or `111` for **Play! 1.11** ### Play! configuration with application.conf @@ -53,13 +53,13 @@ It means that you can define special keys in your `application.conf` file that w You can for example: -* set production mode so the files are compiled at startup time and the errors are logged in a file: +- set production mode so the files are compiled at startup time and the errors are logged in a file: ```bash %clevercloud.application.mode=prod ``` -* set up a mysql database (using environment variables) +- set up a mysql database (using environment variables) ```bash %clevercloud.db.url="jdbc:mysql://"${MYSQL_ADDON_HOST}"/"${MYSQL_ADDON_DB} diff --git a/content/doc/applications/scala/play-framework-2.md b/content/doc/applications/scala/play-framework-2.md index bfadbb9de..d3617d1cd 100644 --- a/content/doc/applications/scala/play-framework-2.md +++ b/content/doc/applications/scala/play-framework-2.md @@ -32,7 +32,7 @@ Clever Cloud supports Play! 2 applications natively. The following guide explain ### Mandatory configuration -* Starting from **Play 2.4**, your application needs **Java 8** to run. Please read [select java version](/doc/applications/java/java-war/#available-java-versions "Select Java version") for more information. +- Starting from **Play 2.4**, your application needs **Java 8** to run. Please read [select java version](/doc/applications/java/java-war/#available-java-versions "Select Java version") for more information. Play! 2 applications use sbt. Please have a look at [deploy Scala apps](/doc/applications/scala) for a complete documentation on sbt deployment options. @@ -40,9 +40,9 @@ Play! 2 applications use sbt. Please have a look at [deploy Scala apps](/doc/app To deploy a Play! application you have to set a secret in your `conf/application.conf` the environment variable name depends on your Play2! version: -* 2.6.x: `play.http.secret.key=${?APPLICATION_SECRET}`, to generate secret use `sbt playGenerateSecret`; -* 2.4.x -> 2.5.x: `play.crypto.secret=${?APPLICATION_SECRET}` -> `sbt playGenerateSecret`; -* 2.3.x: `application.secret=${?APPLICATION_SECRET}` -> `sbt play-generate-secret`. +- 2.6.x: `play.http.secret.key=${?APPLICATION_SECRET}`, to generate secret use `sbt playGenerateSecret`; +- 2.4.x -> 2.5.x: `play.crypto.secret=${?APPLICATION_SECRET}` -> `sbt playGenerateSecret`; +- 2.3.x: `application.secret=${?APPLICATION_SECRET}` -> `sbt play-generate-secret`. Then, in your Clever Cloud application define `APPLICATION_SECRET` [environment variable](#setting-up-environment-variables-on-clever-cloud) with the generated value. @@ -87,7 +87,7 @@ To expose an environment variable through Play configuration, add `my.option=${? If your project fails with the error `sbt.ResolveException: unresolved dependency: play#sbt-plugin;2.0: not found` this is because some versions of Play2 try to retrieve a nonexistent version of "sbt-plugin" which is required by the framework. You have two options to fix this problem: -* You can set the "play.version" environment variable in the `/clevercloud/sbt.json` file. For example, for Play 2.0.4: +- You can set the "play.version" environment variable in the `/clevercloud/sbt.json` file. For example, for Play 2.0.4: ``` javascript { @@ -97,7 +97,7 @@ You have two options to fix this problem: } ``` -* You can modify `plugins.sbt` in the project folder of your app like the following: +- You can modify `plugins.sbt` in the project folder of your app like the following: ``` scala // Comment to get more information during initialization diff --git a/content/doc/applications/static-apache.md b/content/doc/applications/static-apache.md index f327d8ede..5ad209fb3 100644 --- a/content/doc/applications/static-apache.md +++ b/content/doc/applications/static-apache.md @@ -90,9 +90,11 @@ To use it with our static applications, you need a `.htaccess` file like this at ``` + You can find your `PRERENDER_TOKEN` on your [Prerender.io account](https://prerender.io/). You can verify your configuration work using [this guide](https://docs.prerender.io/docs/how-to-test-your-site-after-you-have-successfully-validated-your-prerender-integration). + ## Apache Configuration with `CC_WEBROOT` If you set the `CC_WEBROOT = /` environment variable, make sure you put your `.htaccess` file at the root of your `/`. This is where Apache will look for directives when you deploy an application in a Static runtime. @@ -100,6 +102,7 @@ If you set the `CC_WEBROOT = /` environment variable, make sure you If you don't set the [`CC_WEBROOT`](/doc/reference/reference-environment-variables/#php) environment variable, the root of your project is the root of your web server. ## 🎓 Static Site Generators (SSG) guides + {{% content-raw "static-guides" %}} {{% content "request-flow" %}} diff --git a/content/doc/applications/static.md b/content/doc/applications/static.md index ac0bd5dc4..bc98ce195 100644 --- a/content/doc/applications/static.md +++ b/content/doc/applications/static.md @@ -41,6 +41,7 @@ To create a new Static application, use the [Clever Cloud Console](https://conso ```bash clever create --type static ``` + - [Learn more about Clever Tools](/doc/cli/) - [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) @@ -143,6 +144,7 @@ Supported Static Site Generators (SSG) are: - Detected file: `config.toml` ## 🎓 Static Site Generators (SSG) guides + {{% content-raw "static-guides" %}} {{% content "url_healthcheck" %}} diff --git a/content/doc/applications/v.md b/content/doc/applications/v.md index 4d4816d49..0cae3c86d 100644 --- a/content/doc/applications/v.md +++ b/content/doc/applications/v.md @@ -26,6 +26,7 @@ To create a new V (Vlang) application, use the [Clever Cloud Console](https://co ```bash clever create --type v ``` + - [Learn more about Clever Tools](/doc/cli/) - [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) diff --git a/content/doc/best-practices/load-testing.md b/content/doc/best-practices/load-testing.md index b7e1ffc50..1666e6e29 100644 --- a/content/doc/best-practices/load-testing.md +++ b/content/doc/best-practices/load-testing.md @@ -19,9 +19,9 @@ Performing a performance test is the best way to determine how your application In general, these tests make it possible to identify: -* the maximum number of concurrent users -* whether the current resources are sufficient to operate your application in good conditions -* and the maximum operational capacity of your application +- the maximum number of concurrent users +- whether the current resources are sufficient to operate your application in good conditions +- and the maximum operational capacity of your application ## Considerations to take into account @@ -33,19 +33,19 @@ In case you have dedicated frontends, note that too much load could impact your Performing these tests will allow you: -* to have perspective on the performance and stability of your infrastructure. -* to be able to identify bottlenecks in particular stress scenarios -* to identify and remove any weaknesses in architectures via supervision and performance management strategies and increase scalability +- to have perspective on the performance and stability of your infrastructure. +- to be able to identify bottlenecks in particular stress scenarios +- to identify and remove any weaknesses in architectures via supervision and performance management strategies and increase scalability The determining elements are: -* The response time of your transactions (poorly optimized requests, etc.) -* The performance of the application (single-threaded application, expectations of external services, generation of logs in a file, etc.) -* The performance of managed services such as databases (lack of indexes, unnecessary iterations, etc.) -* Software design in general -* Hardware limitations such as CPU, RAM, network, etc. -* bad middleware configurations (databases, server, etc.) -* The response time between the client and the application +- The response time of your transactions (poorly optimized requests, etc.) +- The performance of the application (single-threaded application, expectations of external services, generation of logs in a file, etc.) +- The performance of managed services such as databases (lack of indexes, unnecessary iterations, etc.) +- Software design in general +- Hardware limitations such as CPU, RAM, network, etc. +- bad middleware configurations (databases, server, etc.) +- The response time between the client and the application ## Online and local load tools @@ -55,15 +55,15 @@ Choosing a load-testing solution will depend on several factors. If simplicity a Online services are available to perform your tests. Most offer typical user journey scenarios to allow you to simulate a user: -* [K6](https://k6.io) -* [Octoperf](https://octoperf.com) -* [Loader.io](https://loader.io) +- [K6](https://k6.io) +- [Octoperf](https://octoperf.com) +- [Loader.io](https://loader.io) ### Local, with open source alternatives Some open source services to be installed on an infrastructure (workstation or IaaS in the best case) are also available: -* [Gatling](https://gatling.io) -* [Drill](https://github.com/fcsonline/drill) -* [Locust](https://locust.io) -* [Jmeter](https://jmeter.apache.org) +- [Gatling](https://gatling.io) +- [Drill](https://github.com/fcsonline/drill) +- [Locust](https://locust.io) +- [Jmeter](https://jmeter.apache.org) diff --git a/content/doc/best-practices/tips_and_tricks.md b/content/doc/best-practices/tips_and_tricks.md index 6bd44a18c..9492c1642 100644 --- a/content/doc/best-practices/tips_and_tricks.md +++ b/content/doc/best-practices/tips_and_tricks.md @@ -23,18 +23,18 @@ The [Clever Cloud Console](https://console.clever-cloud.com) is your control cen The left sidebar menu includes a powerful search function. Beyond simple name searches, you can use special keywords to filter your resources: -| Keyword | What it Shows | -| ------- | ----------- | -| `is:addon` | Lists all your add-ons | -| `is:app` | Shows only applications | +| Keyword | What it Shows | +| -------------- | --------------------------------------------------------------------------------- | +| `is:addon` | Lists all your add-ons | +| `is:app` | Shows only applications | | `is:[addon]` | Filters specific add-on types (Examples: `is:postgresql`, `is:redis`, `is:mysql`) | -| `is:[runtime]` | Shows applications by runtime (Examples: `is:node`, `is:php`, `is:java`) | +| `is:[runtime]` | Shows applications by runtime (Examples: `is:node`, `is:php`, `is:java`) | ### OpenSearch support Clever Cloud supports OpenSearch, allowing you to add [the Console](https://console.clever-cloud.com) as a search engine in your browser, such as [Chrome](https://support.google.com/chrome/answer/95426) or [Firefox](https://support.mozilla.org/kb/add-or-remove-search-engine-firefox). Then, you can quickly search for applications, add-ons or organisations directly from your address bar. For example, add: -``` +```yaml name: Clever Cloud shortcut: cc search URL: https://console.clever-cloud.com/search?q=%s @@ -103,17 +103,17 @@ Press the `/` key anywhere in the Console to search for applications, add-ons, o Use these shortcuts when viewing applications or add-ons: -| Key | Takes You To | -| --- | ----------- | +| Key | Takes You To | +| --- | ----------------------- | | `?` | Keyboard shortcuts help | -| `a` | Application activity | -| `d` | Domain settings | -| `e` | Environment variables | -| `i` | Resource information | -| `l` | Logs view | -| `m` | Application metrics | -| `o` | Overview page | -| `s` | Scalability settings | +| `a` | Application activity | +| `d` | Domain settings | +| `e` | Environment variables | +| `i` | Resource information | +| `l` | Logs view | +| `m` | Application metrics | +| `o` | Overview page | +| `s` | Scalability settings | ### Running One-Time Tasks @@ -303,7 +303,6 @@ Each application maintains: - Individual environment variables - Automatic TLS certificate generation - [Path routing documentation](/doc/administrate/domain-names/#path-routing) ### Create Custom Add-ons @@ -326,7 +325,6 @@ The `cleverapps.io` domain system offers: Remove these domains when moving to production with your custom domain. - ### API-First Platform Clever Cloud's entire platform runs on its public API: diff --git a/content/doc/billing/_index.md b/content/doc/billing/_index.md index 060038253..2b886776a 100644 --- a/content/doc/billing/_index.md +++ b/content/doc/billing/_index.md @@ -23,6 +23,6 @@ aliases: {{< cards >}} {{< card link="/developers/doc/billing/analytics-consumption" title="Analytics and consumption" icon="chart-bar" >}} {{< card link="/developers/doc/billing/payments-invoicing" title="Payments & invoicing" icon="banknotes" >}} - {{< card link="https://www.clever.cloud/pricing/" title="Pricing" icon="currency-euro" >}} + {{< card link="" title="Pricing" icon="currency-euro" >}} {{< card link="/developers/doc/billing/unified-invoices" title="Unified invoicing" icon="user-group" >}} {{< /cards >}} diff --git a/content/doc/billing/analytics-consumption.md b/content/doc/billing/analytics-consumption.md index 9e5e3f940..6e158464b 100644 --- a/content/doc/billing/analytics-consumption.md +++ b/content/doc/billing/analytics-consumption.md @@ -20,10 +20,10 @@ Clever Cloud's billing is based on several types of products: applications (Java The calculation of consumption is based on the second of running. Once your monthly bill has been generated, you can refer to it to find out by service: -* the size of the instances used -* the execution time -* the unit price per second -* the total monthly amount +- the size of the instances used +- the execution time +- the unit price per second +- the total monthly amount To find your consumption history, it is visible in the **Metrics in Grafana** service via a specific Grafana dashboard. This is called the **Uptime Service**. diff --git a/content/doc/billing/payments-invoicing.md b/content/doc/billing/payments-invoicing.md index 66bf154a8..5acf2c3ad 100644 --- a/content/doc/billing/payments-invoicing.md +++ b/content/doc/billing/payments-invoicing.md @@ -19,10 +19,10 @@ aliases: In the sidebar of the organisation section, there are three alternatives to buy credit for your applications: -* Credit card (powered by Stripe) -* Paypal -* Bank transfer: we accept international bank transfers. This option will generate a reference that you will have to add to your bank transfert, as a note. -* SEPA Direct Debit: we support payment via SEPA debit. More information in the [SEPA Direct Debit section](#sepa-direct-debit) +- Credit card (powered by Stripe) +- Paypal +- Bank transfer: we accept international bank transfers. This option will generate a reference that you will have to add to your bank transfert, as a note. +- SEPA Direct Debit: we support payment via SEPA debit. More information in the [SEPA Direct Debit section](#sepa-direct-debit) ## Invoices @@ -36,9 +36,9 @@ Also an organisation member with the accountant role can receive invoices as des ## Change Billing Information -* for personal account: available in *Profile > Informations* +- for personal account: available in *Profile > Informations* -* for organisation: available in *Information > Billing details* +- for organisation: available in *Information > Billing details* ![Clever Cloud Console: "Information menu from the organisation"](/images/billing-infos.png "organisation information") @@ -58,9 +58,9 @@ Mandate: > > Debit instructions are issued in the following cases: > -> * when you confirm an invoice payment; -> * starting 5 days after issuing the invoice, would you set your IBAN as default payment method; -> * would the first debit fail, new attempts will be made every day up to 15 days after invoice issuance and every 5 days after that. +> - when you confirm an invoice payment; +> - starting 5 days after issuing the invoice, would you set your IBAN as default payment method; +> - would the first debit fail, new attempts will be made every day up to 15 days after invoice issuance and every 5 days after that. > Your rights are explained in a statement that you can obtain from your bank. ### About invoices issuance and notifications diff --git a/content/doc/billing/unified-invoices.md b/content/doc/billing/unified-invoices.md index 6b199a215..784b3359a 100644 --- a/content/doc/billing/unified-invoices.md +++ b/content/doc/billing/unified-invoices.md @@ -23,9 +23,9 @@ At the beginning of each month, a new invoice is generated for each organisation The invoice is made up of three distinct sections: -* A summary of the total amount to be paid for the current month -* An exploded view of the invoice calculation, including credits used, coupons, provision for the coming month etc. -* and full details of the operating time of each service, of each service invoiced, of storage used etc. +- A summary of the total amount to be paid for the current month +- An exploded view of the invoice calculation, including credits used, coupons, provision for the coming month etc. +- and full details of the operating time of each service, of each service invoiced, of storage used etc. To find your invoices, go to your organisation and click on **Invoices** to see a list of them and their payment status: @@ -41,14 +41,14 @@ This first section shows the fee for the use of Clever Cloud over a month. Any d This part details line by line the evolution of your credit with: -* The balance of free credits (a) -* Pre-paid credits (b) -* The use of free credits (c) -* Expired credits (d) -* Use of your prepaid credits (e) -* Estimated consumption for the next period (f) -* Invoiced credits pending payment (g) -* Amount of prepaid credits to be topped up (h) +- The balance of free credits (a) +- Pre-paid credits (b) +- The use of free credits (c) +- Expired credits (d) +- Use of your prepaid credits (e) +- Estimated consumption for the next period (f) +- Invoiced credits pending payment (g) +- Amount of prepaid credits to be topped up (h) The **amount of prepaid credits to be topped up** and the **new balance of credits after settlement** are explained via calculations of the different types of credits and their use over time. @@ -65,8 +65,8 @@ This section of the invoice details the usage of each cloud service. While applications are billed on a per second basis, this may be different for managed services. From a billing point of view there are X categories -* time-based billing (per second) -* resource consumption billing +- time-based billing (per second) +- resource consumption billing For example, dedicated databases are charged by the second, just like applications. In contrast, services such as Cellar object storage or Pulsar are charged according to the volume of data stored and the volume of outgoing traffic. @@ -83,10 +83,10 @@ For each organisation it is possible to register one or more payment methods ([s As long as an invoice remains *pending*, we'll perform these actions on specific days: -* Try to issue an automatic payment using your favorite payment method everyday from the +- Try to issue an automatic payment using your favorite payment method everyday from the 5th to the 15th day after issuing the invoice, then every 5 days after that. -* Send a reminder via email every 5 days if the payment remains unsuccessful. -* Shutdown of your services everyday starting day 31 after invoice issue. +- Send a reminder via email every 5 days if the payment remains unsuccessful. +- Shutdown of your services everyday starting day 31 after invoice issue. This schedule can adapt according to your company's payment delays. Please reach out to us via the support to ensure the best experience. diff --git a/content/doc/ci-cd/custom-scripts.md b/content/doc/ci-cd/custom-scripts.md index 3e2ef919b..9d7957c41 100644 --- a/content/doc/ci-cd/custom-scripts.md +++ b/content/doc/ci-cd/custom-scripts.md @@ -61,7 +61,7 @@ before_script: # Download clever-tools before any script executes ## 🎓 Go further {{< cards >}} - {{< card link="https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables" title="GitHub variables" subtitle="List of GitHub default CI/CD variables" icon="github" >}} - {{< card link="https://docs.gitlab.com/ee/ci/variables/predefined_variables.html" title="GitLab variables" subtitle="List of GitLab predefined variables" icon="gitlab" >}} + {{< card link="" title="GitHub variables" subtitle="List of GitHub default CI/CD variables" icon="github" >}} + {{< card link="" title="GitLab variables" subtitle="List of GitLab predefined variables" icon="gitlab" >}} {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Deploy and manage your applications and dependencies from your terminal." icon="command-line" >}} {{< /cards >}} diff --git a/content/doc/ci-cd/github.md b/content/doc/ci-cd/github.md index 9684cea56..32328fa6e 100644 --- a/content/doc/ci-cd/github.md +++ b/content/doc/ci-cd/github.md @@ -40,6 +40,7 @@ on: types: [opened, closed, synchronize, reopened] branches: [ main ] ``` + Then, use the action and define the mandatory input: ```yaml @@ -68,7 +69,6 @@ name: Create review app set-env: true # Enables the command to set en vars on Clever Cloud ``` - {{% content "ci-cd-configuration" %}} Full instructions are available on the [Action project](https://github.com/CleverCloud/clever-cloud-review-app). @@ -84,6 +84,6 @@ If you encounter troubles or bugs using the GitHub Action, feel free to open an ## 🎓 Go further {{< cards >}} - {{< card link="https://docs.github.com/en/actions" title="GitHub Actions documentation" subtitle="Find help in GitHub Actions documentation" icon="github" >}} + {{< card link="" title="GitHub Actions documentation" subtitle="Find help in GitHub Actions documentation" icon="github" >}} {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Deploy and manage your applications and dependencies from your terminal." icon="command-line" >}} {{< /cards >}} diff --git a/content/doc/ci-cd/gitlab.md b/content/doc/ci-cd/gitlab.md index ae5ed726f..f58af701a 100644 --- a/content/doc/ci-cd/gitlab.md +++ b/content/doc/ci-cd/gitlab.md @@ -86,6 +86,6 @@ If you encounter troubles or bugs using the GitLab component, feel free to open ## 🎓 Go further {{< cards >}} - {{< card link="https://docs.gitlab.com/ee/ci/components" title="GitLab Components" subtitle="Learn how to use GitLab components" icon="gitlab" >}} + {{< card link="" title="GitLab Components" subtitle="Learn how to use GitLab components" icon="gitlab" >}} {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Deploy and manage your applications and dependencies from your terminal." icon="command-line" >}} {{< /cards >}} diff --git a/content/doc/cli/_index.md b/content/doc/cli/_index.md index eb29d6731..c97f6ee39 100644 --- a/content/doc/cli/_index.md +++ b/content/doc/cli/_index.md @@ -65,14 +65,14 @@ You'll find below the first commands to know to connect Clever Tools to your acc To show Clever tools available commands, use: -``` +```console clever clever help ``` For each of them, you can add these parameters: -``` +```console [--help, -?] Display help about this program (default: false) [--version, -V] Display the version of this program (default: false) [--color] Choose whether to print colors or not. You can also use --no-color (default: true) @@ -89,20 +89,20 @@ Some features are available as experimental, before they're completely ready for Experimental features can be (de)activated on-demand. To list them, use: -``` +```console clever features ``` To (de)activate an experimental feature, use: -``` +```console clever features enable theFeature clever features disable theFeature ``` To get information about how to use an experimental feature, use: -``` +```console clever features info theFeature ``` @@ -110,7 +110,7 @@ clever features info theFeature To check the current version or get information about your setup, use: -``` +```console clever version clever diag clever diag --format json @@ -123,7 +123,7 @@ clever diag --format json To connect to your Clever Cloud account, use: -``` +```console clever login ``` @@ -131,7 +131,7 @@ It will open your default browser and start an Open Authorization ([OAuth](https If you already know them, you can use: -``` +```console clever login --secret SECRET --token TOKEN ``` @@ -140,7 +140,7 @@ clever login --secret SECRET --token TOKEN To log out, delete this file or use: -``` +```console clever logout clever logout --alias ALIAS ``` @@ -149,7 +149,7 @@ clever logout --alias ALIAS To get information about the current logged-in user (ID, name, email, 2FA activation, etc.), use: -``` +```console clever profile clever profile open clever profile -F json @@ -161,32 +161,32 @@ To manage multiple profiles or configure per-profile overrides, see: [Profiles a To list primary email and secondary emails associated with your Clever Cloud account, you can use: -``` +```console clever emails clever emails -F json ``` To open the email management page in your browser, use: -``` +```console clever emails open ``` To add a secondary email, use: -``` +```console clever emails add email@example.com ``` To set a secondary email as primary, use: -``` +```console clever emails primary email@example.com ``` To remove one or all secondary emails, use: -``` +```console clever emails remove email@example.com clever emails remove-all clever emails remove-all --yes @@ -196,26 +196,26 @@ clever emails remove-all --yes To list public SSH keys associated with your Clever Cloud account, you can use: -``` +```console clever ssh-keys clever ssh-keys -F json ``` To open the public SSH keys management page in your browser, use: -``` +```console clever ssh-keys open ``` To add a new public SSH key, use: -``` +```console clever ssh-keys add myPublicKey ~/.ssh/id_ecdsa.pub ``` To remove one or all public SSH keys, use: -``` +```console clever ssh-keys remove myPublicKey clever ssh-keys remove-all clever ssh-keys remove-all --yes @@ -232,26 +232,26 @@ To use our public API, you need to be authenticated for most endpoints. If you'r You can query [Clever Cloud public API](/api/) with a bearer token thanks to the Auth Bridge. To create a token, use: -``` +```console clever tokens create myTokenName clever tokens create myTokenName --expiration 2w --format json ``` Once created, you can use it replacing the API endpoint with `https://api-bridge.clever-cloud.com`. For example: -``` +```console curl https://api-bridge.clever-cloud.com/v2/self -H "Authorization: Bearer myToken" ``` To list all your tokens, use: -``` +```console clever tokens clever tokens -F json ``` To revoke a token, use: -``` +```console clever tokens revoke myTokenId ``` diff --git a/content/doc/cli/addons.md b/content/doc/cli/addons.md index 374b3807d..b6981f1eb 100644 --- a/content/doc/cli/addons.md +++ b/content/doc/cli/addons.md @@ -15,7 +15,7 @@ keywords: Add-ons on Clever Cloud are databases, storage services, tools or third party services you can enable through `clever addon provider`. For each of the following commands, you can target a specific user/organisation: -``` +```console [--org, -o, --owner] Organisation ID (or name, if unambiguous) ``` @@ -23,7 +23,7 @@ Add-ons on Clever Cloud are databases, storage services, tools or third party se To list provisioned add-ons, use: -``` +```console clever addon clever addon list clever addon list --format json @@ -33,7 +33,7 @@ clever addon list --format json To use add-ons, you need to identify the corresponding provider. To get information about them (plans, regions, versions), use: -``` +```console clever addon providers clever addon providers show PROVIDER_NAME ``` @@ -42,13 +42,13 @@ clever addon providers show PROVIDER_NAME To create an add-on, select a provider and choose a name: -``` +```console clever addon create PROVIDER ADDON_NAME ``` You can set `plan`, `region`, `version`, `option` and directly `link` an add-on to an application through these parameters: -``` +```console [--link, -l] ALIAS Link the created add-on to the app with the specified alias [--yes, -y] Skip confirmation even if the add-on is not free (default: false) [--plan, -p] PLAN Add-on plan, depends on the provider @@ -63,13 +63,13 @@ You can set `plan`, `region`, `version`, `option` and directly `link` an add-on To rename an add-on, use: -``` +```console clever addon rename ADDON_ID_OR_NAME ADDON_NEW_NAME ``` To delete an add-on, use: -``` +```console clever addon delete [--yes, -y] ADDON_ID_OR_NAME ``` @@ -77,7 +77,7 @@ clever addon delete [--yes, -y] ADDON_ID_OR_NAME Each add-on comes with environment variables. To get them, use: -``` +```console clever addon env [--format, -F] FORMAT ADDON_ID ``` @@ -88,7 +88,7 @@ For some add-ons, an interface URL, default credentials or other instructions ca If you're testing [Materia KV](/doc/addons/materia-kv/), our next generation of serverless distributed database, synchronously-replicated, compatible with Redis® protocol, you can create an add-on and immediately use it: -``` +```console clever addon create kv ADDON_NAME source <(clever addon env ADDON_ID --format shell) redis-cli -h $KV_HOST -p $KV_PORT --tls @@ -96,7 +96,8 @@ redis-cli -h $KV_HOST -p $KV_PORT --tls > [!TIP] > If you use the Fish shell, you can use the following command to set the environment variables: -> ``` +> +> ```console > clever addon env ADDON_ID --format shell | source > ``` @@ -104,14 +105,14 @@ redis-cli -h $KV_HOST -p $KV_PORT --tls Configuration Providers are add-ons that store environment variables you can share across multiple applications through [service dependencies](/doc/cli/services-depedencies/). They have their own dedicated commands: -``` +```console clever config-provider list clever config-provider list --format json ``` To get, set or remove environment variables of a Configuration Provider, use: -``` +```console clever config-provider get CONFIG_PROVIDER_ID_OR_NAME clever config-provider get CONFIG_PROVIDER_ID_OR_NAME --format shell clever config-provider set CONFIG_PROVIDER_ID_OR_NAME VARIABLE_NAME VARIABLE_VALUE @@ -120,14 +121,14 @@ clever config-provider rm CONFIG_PROVIDER_ID_OR_NAME VARIABLE_NAME You can import environment variables from `stdin` (this replaces all existing variables): -``` +```console clever config-provider import CONFIG_PROVIDER_ID_OR_NAME < .env clever config-provider import CONFIG_PROVIDER_ID_OR_NAME --format json < config.json ``` To open the Configuration Provider in Clever Cloud Console, use: -``` +```console clever config-provider open CONFIG_PROVIDER_ID_OR_NAME ``` @@ -138,19 +139,19 @@ clever config-provider open CONFIG_PROVIDER_ID_OR_NAME Databases are backup every day, with last 7 days of backups available to download. You can list them, available formats are: `human` (default) or `json`: -``` +```console clever database backups DATABASE-ID [--format, -F] FORMAT ``` To download one of them, use: -``` +```console clever database backups download [--output, --out] OUTPUT_FILE DATABASE_ID BACKUP_ID ``` This command is still under development and will evolve over time. To get information about backups and download them, you can use our API and `clever curl`. For example: -``` +```console clever curl -X GET https://api.clever-cloud.com/v2/backups// ``` diff --git a/content/doc/cli/applications/_index.md b/content/doc/cli/applications/_index.md index 6edd4b044..6f16b977f 100644 --- a/content/doc/cli/applications/_index.md +++ b/content/doc/cli/applications/_index.md @@ -22,7 +22,7 @@ aliases: You can create a new application on Clever Cloud, linked to your local folder. Only its `type` is required, it should be one of: `docker`, `dotnet`, `elixir`, `frankenphp`, `go`, `gradle`, `haskell`, `jar`, `linux`, `maven`, `meteor`, `node`, `php`, `play1`, `play2`, `python`, `ruby`, `rust`, `sbt`, `static`, `static-apache`, `v` or `war`. Result can be printed in `human` or `json` format. -``` +```console clever create -t TYPE APP_NAME clever create -t TYPE --format json ``` @@ -32,9 +32,9 @@ clever create -t TYPE --format json You can also use one of the following optional parameters: -#### Tasks +### Tasks -``` +```console [--task, -T] COMMAND ``` @@ -47,7 +47,7 @@ You can execute a Task whenever needed through a `clever restart` or modify it a #### GitHub repositories -``` +```console --github owner/repo ``` @@ -57,7 +57,7 @@ If your Clever Cloud account is linked to a GitHub account, you can deploy any o You can ask to deploy your application in a specific organisation/user account, region, with an alias different from its name: -``` +```console [--org, -o, --owner] ID/NAME [--alias, -a] ALIAS [--region, -r] ZONE @@ -84,7 +84,7 @@ Default region is our Paris datacenters (`par`), but it can be: After the application creation, you can ask for a `json` formatted report instead of an `human` sentence: -``` +```console [--format, -F] FORMAT ``` @@ -92,19 +92,19 @@ After the application creation, you can ask for a `json` formatted report instea If an application already exists in your Clever Cloud account, you can link it to any local directory to control it easily. You can even link several applications to a single folder, each one with a different name and/or alias. This is done through the `.clever.json` file. -``` +```console clever link APP_ID_OR_NAME [--alias, -a] ALIAS [--org, -o, --owner] ORG_ID_OR_NAME ``` If multiple applications are linked, you can define one as default with it alias: -``` +```console clever make-default ALIAS ``` To unlink an application: -``` +```console clever unlink ALIAS ``` @@ -112,13 +112,13 @@ clever unlink ALIAS You can list linked applications of a folder, you'll get : ID, alias and deployment URL. -``` +```console clever applications ``` If you only need to get aliases or the result in the JSON format: -``` +```console [--only-aliases] List only application aliases (default: false) [--json, -j] Show result in JSON format (default: false) ``` @@ -127,7 +127,7 @@ If you only need to get aliases or the result in the JSON format: To list all applications across all organisations you have access to, you can use: -``` +```console clever applications list -F json clever applications list --org ORG_ID_OR_NAME ``` @@ -136,7 +136,7 @@ clever applications list --org ORG_ID_OR_NAME To delete an application and unlink it from the current folder you only need to: -``` +```console clever delete clever delete --alias ALIAS clever delete --app APP_ID_OR_NAME diff --git a/content/doc/cli/applications/configuration.md b/content/doc/cli/applications/configuration.md index 5a77adbd5..52300c79b 100644 --- a/content/doc/cli/applications/configuration.md +++ b/content/doc/cli/applications/configuration.md @@ -25,20 +25,20 @@ A Clever Cloud application can easily be configured once created, through follow Each application has options you can get/set: `cancel-on-push`, `description`, `force-https`, `name`, `sticky-sessions`, `task`, `zero-downtime`. -``` +```console clever config get parameter clever config set parameter value ``` To update multiple configuration parameters at a time, use: -``` +```console clever config update FLAGS ``` Available parameters are : -``` +```console [--name] Set name [--description] Set description [--enable-zero-downtime] Enable zero-downtime (default: false) @@ -57,14 +57,14 @@ Available parameters are : Environment variables of Clever Cloud applications can easily be modified: -``` +```console clever env set VARIABLE VALUE clever env rm VARIABLE ``` You can import local environment variables (comma separated) or from a file through `stdin`. If it's a JSON file, each object should be composed of a `name` and a `value`. -``` +```console clever env import-vars VAR_NAME1,VAR_NAME2 clever env import < .env cat .env.json | clever env import --json @@ -72,14 +72,14 @@ cat .env.json | clever env import --json To show or export environment variables of an application, use: -``` +```console clever env clever env > .env ``` You can also export environment variables in a sourceable format (`export ENV_NAME="VALUE";`): -``` +```console clever env --format shell ``` @@ -90,13 +90,13 @@ clever env --format shell By default, a Clever Cloud application gets `app_id.cleverapps.io` as fully qualified domain name ([FQDN](https://fr.wikipedia.org/wiki/Fully_qualified_domain_name)). To see it, use: -``` +```console clever domain ``` To get an overview of domains linked to any of your applications and organisations, use: -``` +```console clever domain overview clever domain overview --filter domain.tld clever domain overview --filter .tld --format json @@ -107,7 +107,7 @@ clever domain overview --filter .tld --format json To add/remove a domain to an application, use: -``` +```console add Add a domain name to a Clever Cloud application rm Remove a domain name from a Clever Cloud application ``` @@ -117,14 +117,14 @@ rm Remove a domain name from a Clever Cloud application To (un)set [the favourite domain](/doc/administrate/domain-names/#primary-favourite-domain-name), use: -``` +```console clever domain favourite set FQDN clever domain favourite unset ``` To check if the domains of an application are properly configured, use: -``` +```console clever domain diag clever domain diag --format json clever domain diag --app APP_ID_OR_NAME @@ -132,7 +132,7 @@ clever domain diag --app APP_ID_OR_NAME You can only diagnose some domains of the application using a text filter: -``` +```console clever domain diag --filter .tld clever domain diag --filter mydomain.tld ``` @@ -144,7 +144,7 @@ clever domain diag --filter mydomain.tld You can easily change the number of instances and `flavor` for an application. It can have a different `flavor` used for build phase, to get it done faster. We also provide horizontal and vertical scaling: you can set a minimal/maximal `flavor` and number of instance, then we autoscale depending on incoming load. To change this, use `clever scale` with the following options: -``` +```console [--flavor] FLAVOR The scale of your application [--min-flavor] MINFLAVOR The minimum scale for your application [--max-flavor] MAXFLAVOR The maximum scale for your application @@ -163,21 +163,21 @@ You can easily change the number of instances and `flavor` for an application. I A Clever Cloud application activate TCP redirections in `default` or `cleverapps` namespace: -``` +```console clever tcp-redirs add --namespace NAMESPACE clever tcp-redirs remove --namespace NAMESPACE PORT ``` To list enabled TCP redirections, use: -``` +```console clever tcp-redirs clever tcp-redirs --format json ``` To list available namespaces, use: -``` +```console clever tcp-redirs list-namespaces ``` diff --git a/content/doc/cli/applications/deployment-lifecycle.md b/content/doc/cli/applications/deployment-lifecycle.md index 998a5c2c7..5fa7ee13e 100644 --- a/content/doc/cli/applications/deployment-lifecycle.md +++ b/content/doc/cli/applications/deployment-lifecycle.md @@ -28,13 +28,13 @@ A Clever Cloud application can easily be deployed and accessed once created, thr Once changes are committed in your local git repository, you can deploy it: -``` +```console clever deploy ``` It will `git push` your code on the remote repository of your application on Clever Cloud automatically. You can, of course, use option to `force push` or use specific local branch for example: -``` +```console [--branch, -b] BRANCH Branch to push (current branch by default) (default: ) [--tag, -t] TAG Tag to push (none by default) (default: ) [--quiet, -q] Don't show logs during deployment (default: false) @@ -50,7 +50,7 @@ It will `git push` your code on the remote repository of your application on Cle Once deployed, you can open the application on your default browser or [Clever Cloud Console](https://console.clever-cloud.com): -``` +```console clever open clever console ``` @@ -59,7 +59,7 @@ clever console To get application state, options or running/scaling status, use: -``` +```console clever status clever status --format json ``` @@ -68,13 +68,13 @@ clever status --format json Once deployed, an application can be restarted: -``` +```console clever restart ``` By default, it will use its build cache when available. But you can override it or use other available options: -``` +```console [--commit] COMMIT ID Restart the application with a specific commit ID [--without-cache] Restart the application without using cache (default: false) [--quiet, -q] Don't show logs during deployment (default: false) @@ -85,7 +85,7 @@ By default, it will use its build cache when available. But you can override it To stop an application or cancel any ongoing deployment, use: -``` +```console clever stop clever cancel-deploy ``` @@ -94,13 +94,13 @@ clever cancel-deploy A Clever Cloud application is a running virtual machine you can ssh to, as a user (`bas`). By default, it will use `OpenSSH` configuration, but you can target a specific identity file: -``` +```console clever ssh [--identity-file, -i] IDENTITY-FILE ``` To ssh a specific application, use: -``` +```console clever ssh --app APP_ID_OR_NAME ``` @@ -108,13 +108,13 @@ clever ssh --app APP_ID_OR_NAME When you deploy an application on Clever Cloud, we collect its logs, hosted in our internal Pulsar stack, all included. To listen to the stream, use: -``` +```console clever logs ``` You can also get logs from a specific timeline, deployment or add-on through options: -``` +```console [--before, --until] BEFORE Fetch logs before this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h) [--after, --since] AFTER Fetch logs after this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h) [--search] SEARCH Fetch logs matching this pattern @@ -127,16 +127,16 @@ You can also get logs from a specific timeline, deployment or add-on through opt When you deploy an application on Clever Cloud, we collect its access logs, hosted in our internal Pulsar stack, all included. To listen to the stream, use: -``` +```console clever accesslogs ``` > [!TIP] -> This now uses our v4 API, it's available as Alpha feature for now. +> This now uses our v4 API, it's available as Alpha feature for now. You can also get access logs from a specific timeline or add-on through options, in multiple formats: -``` +```console [--before, --until] BEFORE Fetch logs before this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h) [--after, --since] AFTER Fetch logs after this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h) [--format, -F] FORMAT Output format (human, json, json-stream) (default: human) @@ -144,14 +144,14 @@ You can also get access logs from a specific timeline or add-on through options, You can for example get access logs in JSON stream format for the last hour with: -``` +```console clever accesslogs --format json-stream --since 1h clever accesslogs -F json-stream | jq '.source.ip' ``` or JSON if you add a date/time end limit: -``` +```console clever accesslogs --app APP_NAME --since 2025-04-21T13:37:42 --until 1d -F json | jq '[.[] | {date, countryCode: .source.countryCode, ip: .source.ip, port: .source.port}]' clever accesslogs --app APP_NAME --since 2025-04-21T13:37:42 --until 1d -F json | jq '.[] | [.date, .source.countryCode, .source.ip, .source.port] | @sh' ``` @@ -163,13 +163,13 @@ clever accesslogs --app APP_NAME --since 2025-04-21T13:37:42 --until 1d -F json To get deployment activity, use: -``` +```console clever activity ``` By default, it will show you last 10 deployments. You can show all or listen to a stream of incoming deployments through options: -``` +```console [--follow, -f] Track new deployments in activity list (default: false) [--show-all] Show all activity (default: false) [--format, -F] FORMAT Output format (human, json, json-stream) diff --git a/content/doc/cli/install/_index.md b/content/doc/cli/install/_index.md index 32c2cceb0..f98c66a5f 100644 --- a/content/doc/cli/install/_index.md +++ b/content/doc/cli/install/_index.md @@ -42,7 +42,7 @@ Clever Cloud CLI is based on Node.js. We thought it to be easily available on an If you use Arch Linux, install packages [from AUR](https://aur.archlinux.org/packages/clever-tools-bin/). If you don't know how to use this, run: -``` +```console git clone https://aur.archlinux.org/clever-tools-bin.git clever-tools cd clever-tools makepkg -si @@ -52,7 +52,7 @@ makepkg -si If you use a GNU/Linux distribution that uses `.rpm` packages like CentOS or Fedora, run: -``` +```console curl -s https://clever-tools.clever-cloud.com/repos/cc-nexus-rpm.repo > /etc/yum.repos.d/cc-nexus-rpm.repo yum update yum install clever-tools @@ -65,7 +65,7 @@ yum install clever-tools If you use a GNU/Linux distribution that uses `.deb` packages like Debian or Ubuntu, run: -``` +```console curl -fsSL https://clever-tools.clever-cloud.com/gpg/cc-nexus-deb.public.gpg.key | gpg --dearmor -o /usr/share/keyrings/cc-nexus-deb.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/cc-nexus-deb.gpg] https://nexus.clever-cloud.com/repository/deb stable main" | tee -a /etc/apt/sources.list apt update @@ -80,7 +80,7 @@ apt install clever-tools If you are using Exherbo, run: -``` +```console cave resolve repository/CleverCloud -zx1 cave resolve clever-tools-bin -zx ``` @@ -89,7 +89,7 @@ cave resolve clever-tools-bin -zx If you use another GNU/Linux distribution, download the `.tar.gz` archive and extract the binary in your `PATH`: -``` +```console curl -O https://clever-tools.clever-cloud.com/releases/latest/clever-tools-latest_linux.tar.gz tar xvzf clever-tools-latest_linux.tar.gz cp clever-tools-latest_linux/clever ~/.local/bin/ @@ -97,7 +97,7 @@ cp clever-tools-latest_linux/clever ~/.local/bin/ > [!TIP] > The packages are available on Clever Cloud's Cellar bucket: [clever-tools-latest_linux.tar.gz](https://clever-tools.clever-cloud.com/releases/latest/clever-tools-latest_linux.tar.gz). \ -> Retrieve any release by replacing `latest` (path and filename) with the version number you need. +> Retrieve any release by replacing `latest` (path and filename) with the version number you need. ## macOS @@ -107,7 +107,7 @@ We only provide macOS packages for Apple Silicon processors. On an Intel Mac, pr If you use macOS and you have [Homebrew](https://brew.sh) installed, run: -``` +```console brew install CleverCloud/homebrew-tap/clever-tools ``` @@ -115,7 +115,7 @@ brew install CleverCloud/homebrew-tap/clever-tools If you use macOS, but you don't have [Homebrew](https://brew.sh) installed, download the `.tar.gz` archive and extract the binary in your `PATH`: -``` +```console curl -O https://clever-tools.clever-cloud.com/releases/latest/clever-tools-latest_macos.tar.gz tar xvzf clever-tools-latest_macos.tar.gz cp clever-tools-latest_macos/clever ~/.local/bin/ @@ -131,7 +131,7 @@ cp clever-tools-latest_macos/clever ~/.local/bin/ If you use Windows run in a terminal: -``` +```console winget install CleverTools ``` @@ -151,9 +151,9 @@ $env:PATH += ";$(Resolve-Path .\clever-tools-latest_win\)" ## Docker -If you are using docker, use the image provided [here](https://hub.docker.com/r/clevercloud/clever-tools/). +If you are using Docker, use the [Clever Tools image from Docker Hub](https://hub.docker.com/r/clevercloud/clever-tools/). -``` +```console docker pull clevercloud/clever-tools docker run --rm clever-tools ``` diff --git a/content/doc/cli/install/nexus-config.md b/content/doc/cli/install/nexus-config.md index 145b48f51..d4c9f7b5a 100644 --- a/content/doc/cli/install/nexus-config.md +++ b/content/doc/cli/install/nexus-config.md @@ -18,17 +18,17 @@ We use a self hosted Nexus repository to distribute `.deb` and `.rpm`. It's avai ### `.deb` repository -We have a repository for `.deb` packages, available [here](https://nexus.clever-cloud.com/#browse/browse:deb). +We provide a [Nexus repository for `.deb` packages](https://nexus.clever-cloud.com/#browse/browse:deb). -* stable: [deb](https://nexus.clever-cloud.com/#browse/browse:deb) +- stable: [deb](https://nexus.clever-cloud.com/#browse/browse:deb) The repository is signed with the PGP key `Clever Cloud Nexus (deb)`. The public key is published on the Cellar at [https://clever-tools.clever-cloud.com/gpg/cc-nexus-deb.public.gpg.key](https://clever-tools.clever-cloud.com/gpg/cc-nexus-deb.public.gpg.key). The private key, public key and passphrase are stored in our vault. ### `.rpm` repository -We have a repository for `.rpm` packages, available [here](https://nexus.clever-cloud.com/#browse/browse:rpm). +We provide a [Nexus repository for `.rpm` packages](https://nexus.clever-cloud.com/#browse/browse:rpm). -* Link to Nexus [yum-repositories docs](https://help.sonatype.com/en/yum-repositories.html) +- Link to Nexus [yum-repositories docs](https://help.sonatype.com/en/yum-repositories.html) The repository is not signed, but the `.rpm` packages are. @@ -38,22 +38,22 @@ The repo description is maintained in this git repo at `templates/rpm/cc-nexus-r Steps to follow to generate a new pair of GnuPG keys: -* Prepare a random string for the passphrase (30+ chars is good) -* Run `gpg --gen-key` -* Name: `Clever Cloud Nexus (deb)` -* Email: ci@clever-cloud.com -* Get the ID of the new generated key with `gpg --list-keys` -* Export the public key in a file - * `gpg --armor --output cc-nexus-deb.public.gpg.key --export ` -* Publish the public key on the Cellar - * `s3cmd -c .s3cfg put --acl-public cc-nexus-deb.public.gpg.key s3://clever-tools.clever-cloud.com/gpg/` -* Export the private key in a file - * `gpg --armor --output cc-nexus-deb.private.gpg.key --export-secret-key ` -* Update the private key where it's needed - * The `deb` private key and passphrase need to be set in both `deb` and `deb-stable` repo in Nexus. - * The `rpm` private key and passphrase need to be set in Jenkins. -* Combine both keys into one file - * `cat cc-nexus-deb.*.gpg.key cc-nexus-deb.combined.gpg.key` -* Add the combined key to the vault along with the random passphrase. +- Prepare a random string for the passphrase (30+ chars is good) +- Run `gpg --gen-key` +- Name: `Clever Cloud Nexus (deb)` +- Email: +- Get the ID of the new generated key with `gpg --list-keys` +- Export the public key in a file + - `gpg --armor --output cc-nexus-deb.public.gpg.key --export ` +- Publish the public key on the Cellar + - `s3cmd -c .s3cfg put --acl-public cc-nexus-deb.public.gpg.key s3://clever-tools.clever-cloud.com/gpg/` +- Export the private key in a file + - `gpg --armor --output cc-nexus-deb.private.gpg.key --export-secret-key ` +- Update the private key where it's needed + - The `deb` private key and passphrase need to be set in both `deb` and `deb-stable` repo in Nexus. + - The `rpm` private key and passphrase need to be set in Jenkins. +- Combine both keys into one file + - `cat cc-nexus-deb.*.gpg.key cc-nexus-deb.combined.gpg.key` +- Add the combined key to the vault along with the random passphrase. This is an example for `deb` but the same goes for `rpm`. diff --git a/content/doc/cli/kv-stores.md b/content/doc/cli/kv-stores.md index 8c321433a..8575361d0 100644 --- a/content/doc/cli/kv-stores.md +++ b/content/doc/cli/kv-stores.md @@ -17,7 +17,7 @@ aliases: If you're using [Materia KV](/doc/addons/materia-kv/), our next generation of key-value databases, serverless, distributed, synchronously-replicated, compatible with the Redis® protocol (and later DynamoDB, GraphQL), you can easily create an add-on with Clever Tools: -``` +```console clever addon create kv ADDON_NAME ``` diff --git a/content/doc/cli/logs-drains.md b/content/doc/cli/logs-drains.md index d2107936f..d8a2cd9f9 100644 --- a/content/doc/cli/logs-drains.md +++ b/content/doc/cli/logs-drains.md @@ -17,7 +17,7 @@ aliases: You can use Clever Tools to control logs drains, through following commands. Each can target a specific application, adding `--app APP_ID_OR_NAME` or a local alias (`--alias`, `-a`): -``` +```console clever drain clever drain -F json clever drain create @@ -30,7 +30,7 @@ clever drain disable All drain subcommands also accept `--addon ADDON_ID_OR_REAL_ID` to target an add-on instead of an application. The `--addon` option is mutually exclusive with `--app` and `--alias`. -``` +```console clever drain --addon postgresql_xxxxxxxx clever drain create --addon postgresql_xxxxxxxx raw-http https://logs.example.com clever drain get --addon postgresql_xxxxxxxx @@ -51,7 +51,7 @@ Where `DRAIN-TYPE` is one of: Drain creation supports the following options: -``` +```console [--username, -u] USERNAME Basic auth username (for elasticsearch or raw-http) [--password, -p] PASSWORD Basic auth password (for elasticsearch or raw-http) [--api-key, -k] API_KEY API key (for newrelic) @@ -67,7 +67,7 @@ ElasticSearch drains use the Elastic bulk API. To match this endpoint, specify ` Datadog has two zones, EU and COM. An account on one zone is not available on the other, make sure to target the good EU or COM intake endpoint. To create a [Datadog](https://docs.datadoghq.com/api/?lang=python#send-logs-over-http) drain, you just need to use one of the following command depending on your zone: -``` +```console # EU clever drain create datadog "https://http-intake.logs.datadoghq.eu/v1/input/?ddsource=clevercloud&service=&host=" # US @@ -80,6 +80,6 @@ The `host` query parameter is not mandatory: in the Datadog pipeline configurati NewRelic has two zones, EU and US. An account on one zone is not available on the other, make sure to target the good EU or US intake endpoint. To create a [NewRelic](https://docs.newrelic.com/docs/logs/log-api/introduction-log-api/) drain, you just need to use: -``` +```console clever drain create newrelic "https://log-api.eu.newrelic.com/log/v1" --api-key ``` diff --git a/content/doc/cli/network-groups.md b/content/doc/cli/network-groups.md index ce8a43de2..13f8ff1e0 100644 --- a/content/doc/cli/network-groups.md +++ b/content/doc/cli/network-groups.md @@ -16,9 +16,9 @@ aliases: [Network Groups](/doc/develop/network-groups/) (NG) are a way to create a private secure network between resources inside Clever Cloud infrastructure, using [WireGuard](https://www.wireguard.com/). It's also possible to connect external resources to a Network Group. There are three components to this feature: -* Network Group: a group of resources that can communicate with each through an encrypted tunnel -* Member: a resource that can be part of a Network Group (`application`, `addon` or `external`) -* Peer: Instance of a resource connected to a Network Group (can be `external`) +- Network Group: a group of resources that can communicate with each through an encrypted tunnel +- Member: a resource that can be part of a Network Group (`application`, `addon` or `external`) +- Peer: Instance of a resource connected to a Network Group (can be `external`) A Network Group is defined by an ID (`ngId`) and a `label`. It can be completed by a `description` and `tags`. @@ -42,13 +42,13 @@ When an application connects to a Network Group, you can reach it on any port in Activate `ng` feature flag to manage Network Groups: -``` +```console clever features enable ng ``` Then, check it works with the following command: -``` +```console clever ng ``` @@ -58,19 +58,19 @@ In all the following examples, you can target a specific organisation with the ` A Network Group is simple to create: -``` +```console clever ng create myNG ``` You can create it declaring its members: -``` +```console clever ng create myNG --link app_xxx,postgresql_xxx ``` You can add a description and tags: -``` +```console clever ng create myNG --description "My first NG" --tags test,ng ``` @@ -78,7 +78,7 @@ clever ng create myNG --description "My first NG" --tags test,ng You can delete a Network Group through its ID or label: -``` +```console clever ng delete ngId clever ng delete ngLabel ``` @@ -87,7 +87,7 @@ clever ng delete ngLabel Once created, you can list your Network Groups: -``` +```console clever ng ┌─────────┬───────-┬─────────-─┬───────────────┬─────────┬───────┐ @@ -103,7 +103,7 @@ A `json` formatted output is available with the `--format/-F json` option. To (un)link an application, add-on or external peer to a Network Group: -``` +```console clever ng link app_xxx ngIdOrLabel clever ng unlink redis_xxx ngIdorLabel ``` @@ -118,13 +118,13 @@ After an unlink, you may need to restart the application to apply the changes. To create an external peer in a Network Group, you need to provide a label, Network Group and WireGuard public key: -``` +```console clever ng create external myExternalPeer myNG wg_public_key ``` To delete an external peer from a Network Group: -``` +```console clever ng delete external peerIdOrLabel myNG ``` @@ -132,20 +132,20 @@ clever ng delete external peerIdOrLabel myNG To get information about a Network Group or a resource (a `json` formatted output is available): -``` +```console clever ng get ngIdOrLabel -F json clever ng get resourceIdOrName ``` You can specify the type of resource to look for with the `--type` option (`NetworkGroup`, `Member`, `CleverPeer`, `ExternalPeer`): -``` +```console clever ng get resourceIdOrName --type ExternalPeer ``` You can also search for Network Groups, members or peers: -``` +```console clever ng search text_to_search -F json clever ng search text_to_search --type Member ``` @@ -158,7 +158,7 @@ clever ng search text_to_search --type Member To get the WireGuard configuration of a peer (a `json` formatted output is available): -``` +```console clever ng get-config peerIdOrLabel myNG ``` @@ -166,6 +166,6 @@ clever ng get-config peerIdOrLabel myNG You can find ready to deploy projects using Network Groups in the following repositories: -- https://github.com/CleverCloud/network-groups-example +- Create your own and [let us know](https://github.com/CleverCloud/Community/discussions/categories/network-groups)! diff --git a/content/doc/cli/notifications-webhooks.md b/content/doc/cli/notifications-webhooks.md index 4244b5ca3..04b60fced 100644 --- a/content/doc/cli/notifications-webhooks.md +++ b/content/doc/cli/notifications-webhooks.md @@ -20,7 +20,7 @@ aliases: When events happen on Clever Cloud, during add-ons or applications lifecycle for example, you can send email notifications or trigger webhooks. For each of the following command, you can list all items and/or target a specific user/organisation through these parameters: -``` +```console [--org, -o, --owner] Organisation ID (or name, if unambiguous) [--list-all] List all notifications for your user or for an organisation with the `--org` option (default: false) ``` @@ -29,20 +29,20 @@ When events happen on Clever Cloud, during add-ons or applications lifecycle for You can send email notifications when [an event occurs](/doc/account/notifications/#available-events). To list them, use: -``` +```console clever notify-email clever notify-email --format json ``` To add a notification process to an application, use: -``` +```console clever notify-email add --notify ||"ORGANISATION" NAME ``` Available options are: -``` +```console [--event] TYPE Restrict notifications to specific event types [--service] SERVICE_ID Restrict notifications to specific applications and add-ons --notify ||"ORGANISATION" Notify a user, a specific email address or the whole organisation (multiple values allowed, comma separated) @@ -50,7 +50,7 @@ Available options are: To delete a notification process, use: -``` +```console clever notify-email remove NOTIFICATION-ID ``` @@ -58,19 +58,19 @@ clever notify-email remove NOTIFICATION-ID You can trigger Webhooks when an event occurs. To list them, use: -``` +```console clever webhooks -F json ``` To add a webhook to an application, use: -``` +```console clever webhooks add NAME URL ``` You can set the format, restrict to a service or [event types](/doc/account/notifications/#available-events) through these parameters: -``` +```console [--format] FORMAT Format of the body sent to the webhook ('raw', 'slack', 'gitter', or 'flowdock') (default: raw) [--event] TYPE Restrict notifications to specific event types [--service] SERVICE_ID Restrict notifications to specific applications and add-ons @@ -78,6 +78,6 @@ You can set the format, restrict to a service or [event types](/doc/account/noti To delete a webhook, use: -``` +```console clever webhooks remove NOTIFICATION-ID ``` diff --git a/content/doc/cli/operators.md b/content/doc/cli/operators.md index 7a0f739f6..9f500c480 100644 --- a/content/doc/cli/operators.md +++ b/content/doc/cli/operators.md @@ -15,13 +15,13 @@ keywords: Operators allow you to deploy services as turnkey solutions on Clever Cloud. They provision resources, configure them and expose an API and tools to ease their management during their lifecycle. To add operators commands to Clever Tools, enable the `operators` feature: -``` +```console clever features enable operators ``` Then, you can use the commands to manage Keycloak, Matomo, Metabase and Otoroshi instances on Clever Cloud. For example to list them deployed services: -``` +```console clever keycloak clever matomo clever metabase @@ -30,31 +30,32 @@ clever otoroshi To get information about a deployed service, use: -``` +```console clever keycloak get myKeycloak clever matomo get matomo_id --format json ``` > [!TIP] > You can target a deployed service by its ID or name. +> ## Service management To restart or rebuild (restart without cache) a deployed service, use: -``` +```console clever metabase restart myMetabase clever otoroshi rebuild otoroshi_id ``` To open the deployed service dashboard in Clever Cloud Console, use: -``` +```console clever keycloak open myKeycloak ``` You can also open the service web management interface or logs of the underlying application: -``` +```console clever otoroshi open logs myOtoroshi clever otoroshi open webui otoroshi_id ``` @@ -63,20 +64,20 @@ clever otoroshi open webui otoroshi_id To check the version of a deployed service, use: -``` +```console clever matomo version check matomo_id clever metabase version check myMetabase --format json ``` To update to a specific version, use: -``` +```console clever keycloak version update myKeycloak --target 24.0.1 ``` To see a list of available versions, don't provide a target version: -``` +```console clever otoroshi version update otoroshi_id ``` @@ -84,7 +85,7 @@ clever otoroshi version update otoroshi_id Keycloak and Otoroshi can be easily linked to a [Network Group](/doc/cli/network-groups/). To enable/disable this feature, use: -``` +```console clever keycloak enable-ng myKeycloak clever otoroshi disable-ng otoroshi_id ``` diff --git a/content/doc/cli/profiles.md b/content/doc/cli/profiles.md index 549b22f64..9d1cf5fda 100644 --- a/content/doc/cli/profiles.md +++ b/content/doc/cli/profiles.md @@ -16,6 +16,7 @@ You can use multiple profiles with Clever Tools, all stored in the configuration > [!TIP] > The configuration file lives in your OS config directory: +> > - Windows: `%APPDATA%\clever-cloud\clever-tools.json` > - Other systems: XDG config directory (typically `~/.config/clever-cloud/clever-tools.json`) diff --git a/content/doc/cli/services-depedencies.md b/content/doc/cli/services-depedencies.md index 5cc783455..e6f94822a 100644 --- a/content/doc/cli/services-depedencies.md +++ b/content/doc/cli/services-depedencies.md @@ -20,7 +20,7 @@ Each can target a specific application, adding `--app APP_ID_OR_NAME` or a local To list exposed configuration, use: -``` +```console clever published-config clever published-config -F json clever published-config --format shell @@ -28,13 +28,13 @@ clever published-config --format shell To configure exposed configuration, use: -``` +```console clever published-config COMMAND ``` Available commands are `set`, `rm` (remove) or `import`. The latter reads data from `stdin` so use it as is: -``` +```console clever published-config import < file.config ``` @@ -42,14 +42,14 @@ clever published-config import < file.config To list services dependencies, use: -``` +```console clever service clever service --format json ``` You can filter results through these options. -``` +```console [--only-apps] Only show app dependencies (default: false) [--only-addons] Only show add-on dependencies (default: false) [--show-all] Show all available add-ons and applications (default: false) @@ -57,14 +57,14 @@ You can filter results through these options. To create or delete services dependencies, use: -``` +```console clever service COMMAND ADDON_OR_APP_ID clever service COMMAND ADDON_OR_APP_NAME ``` Available commands are: -``` +```console link-app Add an existing app as a dependency unlink-app Remove an app from the dependencies link-addon Link an existing add-on to this application diff --git a/content/doc/develop/env-variables.md b/content/doc/develop/env-variables.md index e799ee5f5..7080fe99d 100644 --- a/content/doc/develop/env-variables.md +++ b/content/doc/develop/env-variables.md @@ -21,14 +21,14 @@ injected in your application's environment. Variables are defined on a per-application basis. They are defined in four ways: -* By provisioned add-ons linked to applications. The variables names +- By provisioned add-ons linked to applications. The variables names are listed in the add-on's information panel. -* By linked applications with exposed configuration. +- By linked applications with exposed configuration. -* By adding variables yourself in the "Environment variables" panel of your application. +- By adding variables yourself in the "Environment variables" panel of your application. -* Some environment variables are added automatically for each application. +- Some environment variables are added automatically for each application. Please note that if you define or modify environment variables, you will need to redeploy your application to make it use the new variables. @@ -42,28 +42,26 @@ to inform you about the current context of your application and about the applic They can be accessed as classic environment variables. -* `APP_ID`: the ID of the application. Each application has a unique identifier used to +- `APP_ID`: the ID of the application. Each application has a unique identifier used to identify it in our system. This ID is the same than the one you can find in the information section of your application. -* `INSTANCE_ID`: the ID of the current instance (scaler) of your application. It's unique for each +- `INSTANCE_ID`: the ID of the current instance (scaler) of your application. It's unique for each instance of your application and changes every time you deploy it. -* `INSTANCE_TYPE`: The type of the instance (scaler). Its value can be `build` or `production`. +- `INSTANCE_TYPE`: The type of the instance (scaler). Its value can be `build` or `production`. `build` is when your application is being built on a dedicated instance. -* `COMMIT_ID`: the commit ID used as a base to deploy your application. As we remove +- `COMMIT_ID`: the commit ID used as a base to deploy your application. As we remove the `.git` directory before the deployment (to avoid security problems), it can be used to know which version of your application is running on the server. -* `APP_HOME`: The absolute path of your application on the server. Can be used to +- `APP_HOME`: The absolute path of your application on the server. Can be used to create absolute links in your application (e.g. `${APP_HOME}/foo/bar`). -* `CC_PRETTY_INSTANCE_NAME`: A random string name generated for each instance using pokemon names. +- `CC_PRETTY_INSTANCE_NAME`: A random string name generated for each instance using pokemon names. -* `INSTANCE_NUMBER`: See below - -[dedicated-instance]: /developers/doc/administrate/apps-management#edit-application-configuration +- `INSTANCE_NUMBER`: See below ### What is the `INSTANCE_NUMBER` variable used for? @@ -79,14 +77,14 @@ It's handy if you want to only run crons on 1 instance (e.g. only on instance 0) We use environment variables for some settings: -* `CC_DISABLE_BUILD_CACHE_UPLOAD`: allows you to ask that the cache archive is neither +- `CC_DISABLE_BUILD_CACHE_UPLOAD`: allows you to ask that the cache archive is neither created nor uploaded after the build process. Thus, restarts won't be speeded up, as the build process will be launched each time. -* `IGNORE_FROM_BUILDCACHE`: allows you to specify paths to ignore when the build +- `IGNORE_FROM_BUILDCACHE`: allows you to specify paths to ignore when the build cache archive is created. Must be relative to your application root. (e.g. `foo/bar:foo/baz` where `bar` or `baz` can be either a directory or a file) -* `CC_OVERRIDE_BUILDCACHE`: allows you to specify paths that will be in the build cache. +- `CC_OVERRIDE_BUILDCACHE`: allows you to specify paths that will be in the build cache. Only those files/directories will be cached (e.g. `foo/bar:foo/baz` where `bar` or `baz` can be either a directory or a file). @@ -97,24 +95,24 @@ examples of the formats available on our different modes. ### Rules -* Letters (ASCII only, upper and lower case) - * Valid: `user_id`, `USER_ID`, `UsERId`, `userid`, `USERID` - * Invalid: `user_id?`, `?userid`, `user.id`, `user id` -* Digits (but not for first character) - * Valid: `user2id`, `userid42` - * Invalid: `2userid`, `1user_Id` -* Underscores - * Valid: `user_id`, `all_user_id`, `_user_id`, `_user_id__` -* Everything else is not allowed +- Letters (ASCII only, upper and lower case) + - Valid: `user_id`, `USER_ID`, `UsERId`, `userid`, `USERID` + - Invalid: `user_id?`, `?userid`, `user.id`, `user id` +- Digits (but not for first character) + - Valid: `user2id`, `userid42` + - Invalid: `2userid`, `1user_Id` +- Underscores + - Valid: `user_id`, `all_user_id`, `_user_id`, `_user_id__` +- Everything else is not allowed #### Java properties rules exception In case of a Java application you can also use: -* Dashes - * Valid: `spring-boot`, `spring-boot-database`, `--spring-boot`, `--spring` -* Dots - * Valid: `spring.boot`, `spring.datasource.url`, `.spring.url` +- Dashes + - Valid: `spring-boot`, `spring-boot-database`, `--spring-boot`, `--spring` +- Dots + - Valid: `spring.boot`, `spring.datasource.url`, `.spring.url` ### Format @@ -127,7 +125,7 @@ The format of an expert variable is `VAR_NAME="VAR_VALUE"`. Here are some examples: -* Multiple variables examples: +- Multiple variables examples: ```bash EMPTY="" @@ -138,7 +136,7 @@ Here are some examples: TWO="value TWO" ``` -* Multiline: +- Multiline: ```bash MULTI="line one @@ -146,7 +144,7 @@ Here are some examples: line three" ``` -* Empty value: +- Empty value: ```bash EMPTY="" @@ -160,7 +158,7 @@ Note: You must put these variables in an array (see the multiple variable exampl Here are some examples: -* Multiline: +- Multiline: ```json { @@ -169,7 +167,7 @@ Here are some examples: } ``` -* Empty value: +- Empty value: ```json { @@ -178,7 +176,7 @@ Here are some examples: } ``` -* Multiple variables examples: +- Multiple variables examples: ```json [ @@ -208,19 +206,19 @@ You can find more information in the documentation pages related to your applica Here is a non-exhaustive summary: -| Language | Usage | -|----------|-------| -| Go | `os.Getenv("MY_VAR")`| -| Haskell | `Os.Getenv["MY_VAR"]` | -| Node.js | `process.env["MY_VAR"]`| -| Java WAR | `System.getProperties().getProperty("MY_VAR")` | -| PHP | `getenv("MY_VAR")` | +| Language | Usage | +| ------------------------------------- | -------------------------------------------------------------- | +| Go | `os.Getenv("MY_VAR")` | +| Haskell | `Os.Getenv["MY_VAR"]` | +| Node.js | `process.env["MY_VAR"]` | +| Java WAR | `System.getProperties().getProperty("MY_VAR")` | +| PHP | `getenv("MY_VAR")` | | Play! Framework 1 & Play! Framework 2 | `System.getenv("MY_VAR")` or `${MY_VAR}` in `application.conf` | -| Python | `os.getenv("MY_VAR")` | -| Ruby | `ENV["MY_VAR"]` | -| Rust | `std::env::var("MY_VAR")` | -| Scala | `System.getenv("MY_VAR")` | -| .NET | `System.Environment.GetEnvironmentVariable("MY_VAR")` | +| Python | `os.getenv("MY_VAR")` | +| Ruby | `ENV["MY_VAR"]` | +| Rust | `std::env::var("MY_VAR")` | +| Scala | `System.getenv("MY_VAR")` | +| .NET | `System.Environment.GetEnvironmentVariable("MY_VAR")` | ## Specific languages diff --git a/content/doc/develop/network-groups.md b/content/doc/develop/network-groups.md index 501b44584..576d170f4 100644 --- a/content/doc/develop/network-groups.md +++ b/content/doc/develop/network-groups.md @@ -15,9 +15,9 @@ aliases: Network Groups (NG) are a way to create a private secure network between resources inside Clever Cloud infrastructure, using [WireGuard](https://www.wireguard.com/). It's also possible to connect external resources to a Network Group. There are three components to this feature: -* Network Group: a group of resources that can communicate with each through an encrypted tunnel -* Member: a resource that can be part of a Network Group (`application`, `addon` or `external`) -* Peer: Instance of a resource connected to a Network Group (can be `external`) +- Network Group: a group of resources that can communicate with each through an encrypted tunnel +- Member: a resource that can be part of a Network Group (`application`, `addon` or `external`) +- Peer: Instance of a resource connected to a Network Group (can be `external`) A Network Group is defined by an ID (`ngId`) and a `label`. It can be completed by a `description` and `tags`. @@ -77,6 +77,7 @@ flowchart TD ## Concepts ### Network Group + Network Groups operate as an overlay network, on top of the existing physical network infrastructure (underlay). When two peers communicate within a Network Group, packets are encapsulated in WireGuard tunnels that traverse Clever Cloud's underlay network. This approach enables the creation of flexible virtual network topologies without modifying the underlying physical infrastructure. Thanks to this overlay architecture, you can establish private and secure communications between your resources, even if they are physically dispersed across different zones of Clever Cloud's infrastructure or outside of it. @@ -84,19 +85,25 @@ This approach enables the creation of flexible virtual network topologies withou Each Network Group has a unique ID (ngId), a human-readable label, and can include an optional description and tags for organisation. ### Members + Resources connected to a Network Group can be applications or add-ons running on Clever Cloud (databases, services, etc.), or external resources outside of Clever Cloud (a third-party server, your local machine). Each member is accessible via a domain name. ### Peers + Individual instances of a member resource. For example, if your application scales to three instances, each instance becomes a peer in the Network Group with its own IP address from the assigned CIDR range. ## Key Benefits + Network Groups offer secure communication with all traffic between resources encrypted using WireGuard. You gain DNS resolution to access resources by domain name within the Network Group, along with internal port access to reach any port on any resource inside the Network Group. This creates a simplified architecture where you can connect applications and their dependencies in a private network. ## Use Cases + Network Groups excel in microservices architecture by connecting multiple services securely without exposing them to the public internet. They simplify database access by allowing applications to access database add-ons through a secure channel. For legacy integration, they help connect external systems securely to your cloud applications. They also provide isolated environments for development and testing. ## Technical Implementation + Network Groups leverage WireGuard, a modern VPN protocol known for its simplicity, speed, and security. When you create a Network Group, a dedicated CIDR range is allocated for your Network Group and WireGuard configurations are generated. When members join, they're assigned DNS names and IPs from the CIDR range, encrypted tunnels are established between peers, and traffic between resources flows through these secure tunnels. ## Best Practices + Create Network Groups based on functional requirements or environments such as production or staging. Use clear, consistent naming for Network Groups and tag them appropriately. Establish different Network Groups to separate security domains, and keep track of which resources belong to which Network Groups. diff --git a/content/doc/develop/oauth2-proxy.md b/content/doc/develop/oauth2-proxy.md index a7f29c82f..f6f555971 100644 --- a/content/doc/develop/oauth2-proxy.md +++ b/content/doc/develop/oauth2-proxy.md @@ -142,12 +142,12 @@ OAUTH2_PROXY_REDIS_CONNECTION_URL="redis://:password@host:port" OAuth2 Proxy logs its configuration errors and exits, so read the deployment logs from the bottom up. The health check reports the public port as closed, which is a consequence of the middleware being down rather than a problem with your application: -| Log message | Cause | -| ----------- | ----- | -| `cookie_secret must be 16, 24, or 32 bytes` | The secret has the wrong length, see [Generate the cookie secret](#generate-the-cookie-secret) | -| `missing setting for email validation` | The configuration carries neither `OAUTH2_PROXY_EMAIL_DOMAINS` nor `OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE` | -| `Your application is not listening on 8080` | OAuth2 Proxy failed to start, the preceding message holds the reason | -| `Some software are not listening as expected: 9000` | OAuth2 Proxy runs, your application doesn't listen on port `9000` | +| Log message | Cause | +| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `cookie_secret must be 16, 24, or 32 bytes` | The secret has the wrong length, see [Generate the cookie secret](#generate-the-cookie-secret) | +| `missing setting for email validation` | The configuration carries neither `OAUTH2_PROXY_EMAIL_DOMAINS` nor `OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE` | +| `Your application is not listening on 8080` | OAuth2 Proxy failed to start, the preceding message holds the reason | +| `Some software are not listening as expected: 9000` | OAuth2 Proxy runs, your application doesn't listen on port `9000` | A login that loops between your application and the identity provider points to `OAUTH2_PROXY_REDIRECT_URL` not matching the callback declared in the provider. A `403 Forbidden` after a successful login points to an email address outside your authorization rules, or to a missing `email` scope. diff --git a/content/doc/develop/request-flow.md b/content/doc/develop/request-flow.md index 8d8c293f5..284f29d9d 100644 --- a/content/doc/develop/request-flow.md +++ b/content/doc/develop/request-flow.md @@ -24,14 +24,14 @@ Request Flow is available for all runtimes except Docker, where you define and m ## Supported services -| Service | Activation | Description | -| ------- | ---------- | ----------- | -| `block` | `CC_REQUEST_FLOW="block"` | Blocks public access with a `200 OK` response. Other ports remain accessible through [Network Groups](/doc/develop/network-groups/) | -| `custom` | `CC_REQUEST_FLOW="custom"` | Any custom reverse proxy, started with `CC_REQUEST_FLOW_CUSTOM` | -| `oauth2-proxy` | `CC_REQUEST_FLOW="oauth2-proxy"` | Authentication proxy using [OAuth2 Proxy](/doc/develop/oauth2-proxy/) | -| `otoroshi-challenge` | `OTOROSHI_CHALLENGE_SECRET` | [Otoroshi](/doc/addons/otoroshi/) challenge verification proxy | -| `redirectionio` | `CC_REDIRECTIONIO_PROJECT_KEY` | HTTP redirects, rewrites, SEO | -| `varnish` | `clevercloud/varnish.vcl` file or `CC_VARNISH_FILE` | HTTP cache accelerator | +| Service | Activation | Description | +| -------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `block` | `CC_REQUEST_FLOW="block"` | Blocks public access with a `200 OK` response. Other ports remain accessible through [Network Groups](/doc/develop/network-groups/) | +| `custom` | `CC_REQUEST_FLOW="custom"` | Any custom reverse proxy, started with `CC_REQUEST_FLOW_CUSTOM` | +| `oauth2-proxy` | `CC_REQUEST_FLOW="oauth2-proxy"` | Authentication proxy using [OAuth2 Proxy](/doc/develop/oauth2-proxy/) | +| `otoroshi-challenge` | `OTOROSHI_CHALLENGE_SECRET` | [Otoroshi](/doc/addons/otoroshi/) challenge verification proxy | +| `redirectionio` | `CC_REDIRECTIONIO_PROJECT_KEY` | HTTP redirects, rewrites, SEO | +| `varnish` | `clevercloud/varnish.vcl` file or `CC_VARNISH_FILE` | HTTP cache accelerator | ## Automatic detection @@ -111,13 +111,13 @@ For an application that manages its own HTTP server, this example produces the f ## Environment variables reference -| Name | Description | -| ---- | ----------- | -| `CC_REQUEST_FLOW` | Comma-separated list of middleware to chain (e.g. `varnish,redirectionio`). Special values: `disable`, `block` | -| `CC_REQUEST_FLOW_CUSTOM` | Command to start a custom middleware. Must contain `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders | -| `CC_REDIRECTIONIO_PROJECT_KEY` | Redirection.io project key. Activates Redirection.io in the request flow | -| `CC_VARNISH_FILE` | Path to a custom Varnish VCL file (default: `clevercloud/varnish.vcl`) | -| `OTOROSHI_CHALLENGE_SECRET` | Otoroshi challenge secret. Activates Otoroshi Challenge verification in the request flow | +| Name | Description | +| ------------------------------ | -------------------------------------------------------------------------------------------------------------- | +| `CC_REQUEST_FLOW` | Comma-separated list of middleware to chain (e.g. `varnish,redirectionio`). Special values: `disable`, `block` | +| `CC_REQUEST_FLOW_CUSTOM` | Command to start a custom middleware. Must contain `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders | +| `CC_REDIRECTIONIO_PROJECT_KEY` | Redirection.io project key. Activates Redirection.io in the request flow | +| `CC_VARNISH_FILE` | Path to a custom Varnish VCL file (default: `clevercloud/varnish.vcl`) | +| `OTOROSHI_CHALLENGE_SECRET` | Otoroshi challenge secret. Activates Otoroshi Challenge verification in the request flow | ## Troubleshooting diff --git a/content/doc/develop/tasks.md b/content/doc/develop/tasks.md index 3713a6af4..a76b831e6 100644 --- a/content/doc/develop/tasks.md +++ b/content/doc/develop/tasks.md @@ -21,6 +21,7 @@ This will guide you in the process of creating a Clever Task: an application tha ### With the Clever Tools CLI Make sure you have [Clever Tools installed](/doc/cli/) and in your project folder, run `clever create --type --task `. You can also add options such as ` --region --org ` where: + 1. `app-name` the name you want for your application, 2. `zone` deployment zone (`par` for Paris or `mtl` for Montreal for example) 3. `org` the organisation ID the application will be created under diff --git a/content/doc/find-help/faq.md b/content/doc/find-help/faq.md index d8faa9391..2a6a19594 100644 --- a/content/doc/find-help/faq.md +++ b/content/doc/find-help/faq.md @@ -27,28 +27,30 @@ When enabling auto-scalability, you have to set a minimum and a maximum of activ {{< /callout >}} ## Which application runtimes are supported by Clever Cloud? + Currently, Clever Cloud supports: -* .NET -* Docker -* Elixir -* Go -* Haskell -* Java (Jar, Maven, Gradle, War/Ear, Play Framework 1 & 2) and Groovy with Gradle -* Linux -* Meteor.js -* Node.js & Bun -* PHP and FrankenPHP -* Python -* Ruby -* Rust -* Scala (SBT, Play Framework 1 & 2) -* Static sites (with or without Apache) -* V (Vlang) +- .NET +- Docker +- Elixir +- Go +- Haskell +- Java (Jar, Maven, Gradle, War/Ear, Play Framework 1 & 2) and Groovy with Gradle +- Linux +- Meteor.js +- Node.js & Bun +- PHP and FrankenPHP +- Python +- Ruby +- Rust +- Scala (SBT, Play Framework 1 & 2) +- Static sites (with or without Apache) +- V (Vlang) See the full list in the [applications documentation](/doc/applications/), or browse the [guides](/guides/) for frameworks and services you can deploy on Clever Cloud. ## How many applications can I create? + As many as you want. We've not set a limited number of apps by developer. ## How to setup domain names I own? @@ -219,7 +221,6 @@ Clever Cloud stores all backups on [Cellar](https://www.clever.cloud/product/cel For custom configurations (for example, multiple retention policies), contact Support. To locate backups not visible in the Console, use [Clever Tools](https://github.com/CleverCloud/clever-tools) with: `clever database backups DATABASE-ID [--format, -F] FORMAT`. Find more [documentation on restoring backups with the CLI](/doc/cli/addons/#database-backups). - ## I can't create my add-on To create add-ons, you need to complete your account information, including your city and ZIP code. diff --git a/content/doc/find-help/support.md b/content/doc/find-help/support.md index 7ed2dafc6..54409b27d 100644 --- a/content/doc/find-help/support.md +++ b/content/doc/find-help/support.md @@ -23,7 +23,7 @@ We provide basic support free-of-charge to all our users, even those who are not Seek help by querying us by email, allowing you to quickly gain answers to your Clever Cloud specific questions and open a ticket directly with our support staff. We will respond to your email within a few hours, or two business days in the worst case. -* +- ## Ticket Center @@ -39,4 +39,4 @@ Clever Cloud offers a premium support option providing 24/7 a direct line to our Get more information and contact our sales team at: -* +- diff --git a/content/doc/find-help/troubleshooting.md b/content/doc/find-help/troubleshooting.md index 8ceaf458b..7dc8019d5 100644 --- a/content/doc/find-help/troubleshooting.md +++ b/content/doc/find-help/troubleshooting.md @@ -134,9 +134,10 @@ You are not allowed to create apps and add-ons, and run your current apps. {{% details title="Possible reasons why your app has been moderated" closed="true" %}} The main reasons why your account has been moderated are: + - You run some forbidden apps such as those described on [this page](https://www.clever.cloud/acceptable-use-policy/) - You have not paid your pending invoices older than 30 days -- Clever Cloud system considers the lack of personal information or missing payment information as suspicious +- Clever Cloud system considers the lack of personal information or missing payment information as suspicious - Our payment platform has spotted you as emitting fraudulent payments. {{% /details %}} @@ -169,16 +170,17 @@ For operations that may exceed the 180-second limit, implement one of these appr 2. Create an asynchronous worker system: move long-running tasks to a background [worker](/doc/develop/workers/) 3. [Purchase a custom load balancer from Clever Cloud](https://www.clever.cloud/fr/contact/) with different timeouts -##### Additional considerations: +### Additional considerations - Design your application architecture to handle timeouts gracefully - Break up long-running operations into smaller tasks Use your embedded [Grafana](/doc/metrics/) to monitor resource usage when implementing any of these solutions. -##### How can I diagnosing Network Issue with `curl` +### Diagnose network issues with `curl` To gather detailed timing information for each step of the connection process, run the following `curl` command: + ``` bash curl -o /dev/null -s -w "DNS resolution: %{time_namelookup}s\nTCP connection: %{time_connect}s\nTLS handshake: %{time_appconnect}s\nTime to first byte: %{time_starttransfer}s\nTotal time: %{time_total}s\n" https:// ``` diff --git a/content/doc/functions/_index.md b/content/doc/functions/_index.md index b918478a7..af7f9a471 100644 --- a/content/doc/functions/_index.md +++ b/content/doc/functions/_index.md @@ -102,6 +102,7 @@ sequenceDiagram ## Prerequisites To use Clever Functions you'll need : + - An authorized access for your organisation - Clever Tools with Functions commands @@ -152,6 +153,7 @@ clever functions deploy --org ?trigger=http ``` diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index a4e44ee99..3520f359b 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -89,11 +89,11 @@ A Kubernetes cluster on Clever Cloud is made of a control plane and one or more You choose how the control plane is distributed at creation time. The topology decides the placement of the Kubernetes components, the available flavors, whether workloads share the same VMs as the control plane, and ultimately what gets billed. Three layouts are available: -|Topology|Customers|VMs you pay for|Flavors|Default node group included?|Typical use case| -|---|---|---|---|---|---| -|`ALL_IN_ONE`|Essential|`replicationFactor` × bundle VM (control plane + integrated worker node). Additional node groups billed separately|`S`, `M`, `L`, `XL`|No node group, but each bundle VM is also a worker node — additional node groups optional|Development, testing, small single-team clusters| -|`DEDICATED_COMPUTE`|Business|`replicationFactor` × control plane VM **plus** every worker VM in your node groups|`XS`, `S`, `M`, `L`, `XL`|No — provision a node group at creation with `--nodegroup` or later|Production clusters where control plane and workloads stay isolated| -|`DISTRIBUTED`|Enterprise|For each of 5 components: `replicationFactor` × component VM **plus** every worker VM in your node groups|`2XS`, `XS`, `S`, `M`, `L`, `XL`|No — provision a node group at creation or later|Demanding production clusters needing fine-grained HA per control plane component| +| Topology | Customers | VMs you pay for | Flavors | Default node group included? | Typical use case | +| ------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ | -------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| `ALL_IN_ONE` | Essential | `replicationFactor` × bundle VM (control plane + integrated worker node). Additional node groups billed separately | `S`, `M`, `L`, `XL` | No node group, but each bundle VM is also a worker node — additional node groups optional | Development, testing, small single-team clusters | +| `DEDICATED_COMPUTE` | Business | `replicationFactor` × control plane VM **plus** every worker VM in your node groups | `XS`, `S`, `M`, `L`, `XL` | No — provision a node group at creation with `--nodegroup` or later | Production clusters where control plane and workloads stay isolated | +| `DISTRIBUTED` | Enterprise | For each of 5 components: `replicationFactor` × component VM **plus** every worker VM in your node groups | `2XS`, `XS`, `S`, `M`, `L`, `XL` | No — provision a node group at creation or later | Demanding production clusters needing fine-grained HA per control plane component | `ALL_IN_ONE` is the default when no topology is specified at cluster creation through Clever Tools or the Console. A replication factor (`1` to `5`) applies to the control plane VMs: the higher the factor, the more resilient the control plane and, in `DEDICATED_COMPUTE` or `DISTRIBUTED`, the more sites it is spread across (up to three Parisian datacenters). In `DISTRIBUTED`, each of the five components has its own flavor and its own replication factor, letting you scale a busy `apiserver` higher than a quieter `scheduler`. @@ -260,7 +260,7 @@ clever k8s nodegroups delete myCluster workers Define a `NodeGroup` resource in a YAML file and apply it: -```yaml{filename="example-nodegroup.yaml"} +```yaml {filename="example-nodegroup.yaml"} apiVersion: api.clever-cloud.com/v1 kind: NodeGroup metadata: @@ -357,7 +357,7 @@ kubectl create secret docker-registry dockerhub-creds \ Reference the secret from your Pod spec via `imagePullSecrets`: -```yaml{filename="deployment-private.yaml"} +```yaml {filename="deployment-private.yaml"} apiVersion: apps/v1 kind: Deployment metadata: @@ -401,7 +401,7 @@ kubectl expose deployment/nginx --type=LoadBalancer --port 80 Alternatively, you can create a YAML file named `deployment-demo.yaml` with the following content and apply it using `kubectl apply -f deployment-demo.yaml`: -```yaml{filename="deployment-demo.yaml"} +```yaml {filename="deployment-demo.yaml"} apiVersion: apps/v1 kind: Deployment metadata: @@ -463,37 +463,37 @@ In practice: The bundle hosts the five control plane components and an integrated worker node on the same VMs. The price below covers everything; additional node groups attached to an `ALL_IN_ONE` cluster are billed separately at the [node group rate](#node-groups-workers). -|Flavor|Resources|Hourly price|Monthly price| -|---|---|---|---| -|S|8 vCPU / 12 GB|0.0889 €|64.00 €| -|M|10 vCPU / 16 GB|0.1167 €|84.00 €| -|L|12 vCPU / 24 GB|0.1667 €|120.00 €| -|XL|16 vCPU / 32 GB|0.2222 €|160.00 €| +| Flavor | Resources | Hourly price | Monthly price | +| ------ | --------------- | ------------ | ------------- | +| S | 8 vCPU / 12 GB | 0.0889 € | 64.00 € | +| M | 10 vCPU / 16 GB | 0.1167 € | 84.00 € | +| L | 12 vCPU / 24 GB | 0.1667 € | 120.00 € | +| XL | 16 vCPU / 32 GB | 0.2222 € | 160.00 € | ### Business (`DEDICATED_COMPUTE`) control plane One VM per replication factor, dedicated to the control plane. -|Flavor|Resources|Hourly price|Monthly price| -|---|---|---|---| -|XS|6 vCPU / 8 GB|0.0917 €|66.00 €| -|S|8 vCPU / 12 GB|0.1333 €|96.00 €| -|M|10 vCPU / 16 GB|0.1750 €|126.00 €| -|L|12 vCPU / 24 GB|0.2500 €|180.00 €| -|XL|16 vCPU / 32 GB|0.3333 €|240.00 €| +| Flavor | Resources | Hourly price | Monthly price | +| ------ | --------------- | ------------ | ------------- | +| XS | 6 vCPU / 8 GB | 0.0917 € | 66.00 € | +| S | 8 vCPU / 12 GB | 0.1333 € | 96.00 € | +| M | 10 vCPU / 16 GB | 0.1750 € | 126.00 € | +| L | 12 vCPU / 24 GB | 0.2500 € | 180.00 € | +| XL | 16 vCPU / 32 GB | 0.3333 € | 240.00 € | ### Enterprise (`DISTRIBUTED`) control plane For each of the five components (`apiserver`, `controller-manager`, `scheduler`, `cloud-controller-manager`, `node-group-operator`), pay `replicationFactor` × the per-VM price below. Each component picks its own flavor and replication factor. -|Flavor|Resources|Hourly price|Monthly price per component| -|---|---|---|---| -|2XS|4 vCPU / 4 GB|0.0500 €|36.00 €| -|XS|6 vCPU / 8 GB|0.0917 €|66.00 €| -|S|8 vCPU / 12 GB|0.1333 €|96.00 €| -|M|10 vCPU / 16 GB|0.1750 €|126.00 €| -|L|12 vCPU / 24 GB|0.2500 €|180.00 €| -|XL|16 vCPU / 32 GB|0.3333 €|240.00 €| +| Flavor | Resources | Hourly price | Monthly price per component | +| ------ | --------------- | ------------ | --------------------------- | +| 2XS | 4 vCPU / 4 GB | 0.0500 € | 36.00 € | +| XS | 6 vCPU / 8 GB | 0.0917 € | 66.00 € | +| S | 8 vCPU / 12 GB | 0.1333 € | 96.00 € | +| M | 10 vCPU / 16 GB | 0.1750 € | 126.00 € | +| L | 12 vCPU / 24 GB | 0.2500 € | 180.00 € | +| XL | 16 vCPU / 32 GB | 0.3333 € | 240.00 € | The price above is **per component VM** (one of the five) at `replicationFactor = 1`. A minimal Distributed cluster with all five components in 2XS at rf=1 therefore costs `5 × 36.00 € = 180.00 €/month` for the control plane alone, plus the [node group rate](#node-groups-workers) for your workers. @@ -501,14 +501,14 @@ The price above is **per component VM** (one of the five) at `replicationFactor Worker VMs in node groups attached to any cluster. An `ALL_IN_ONE` cluster ships with one worker node integrated in each bundle VM (no extra rate); any **additional** node group you attach to an `ALL_IN_ONE` cluster is billed at the rate below, like for `DEDICATED_COMPUTE` and `DISTRIBUTED`. -|Flavor|Resources|Hourly price|Monthly price| -|---|---|---|---| -|2XS|4 vCPU / 4 GB|0.0333 €|24.00 €| -|XS|6 vCPU / 8 GB|0.0611 €|44.00 €| -|S|8 vCPU / 12 GB|0.0889 €|64.00 €| -|M|10 vCPU / 16 GB|0.1167 €|84.00 €| -|L|12 vCPU / 24 GB|0.1667 €|120.00 €| -|XL|16 vCPU / 32 GB|0.2222 €|160.00 €| +| Flavor | Resources | Hourly price | Monthly price | +| ------ | --------------- | ------------ | ------------- | +| 2XS | 4 vCPU / 4 GB | 0.0333 € | 24.00 € | +| XS | 6 vCPU / 8 GB | 0.0611 € | 44.00 € | +| S | 8 vCPU / 12 GB | 0.0889 € | 64.00 € | +| M | 10 vCPU / 16 GB | 0.1167 € | 84.00 € | +| L | 12 vCPU / 24 GB | 0.1667 € | 120.00 € | +| XL | 16 vCPU / 32 GB | 0.2222 € | 160.00 € | ## Clever Kubernetes Operator diff --git a/content/doc/kubernetes/operator.md b/content/doc/kubernetes/operator.md index 39cbc95c7..da1a35460 100644 --- a/content/doc/kubernetes/operator.md +++ b/content/doc/kubernetes/operator.md @@ -18,7 +18,7 @@ The [Clever Kubernetes Operator](https://github.com/CleverCloud/clever-kubernete Modern applications often require a combination of containerized workloads and managed services, such as databases or caches. Managing these resources separately across platforms can become complex and error-prone. The Clever Kubernetes Operator simplifies this process by acting as a bridge, allowing developers to define and interact with Clever Cloud's resources using familiar Kubernetes paradigms. -### Why Use the Clever Kubernetes Operator? +## Why Use the Clever Kubernetes Operator? The Clever Kubernetes Operator was born from a concrete need: to allow users to manage Clever Cloud services directly from a Kubernetes cluster. It reconciles the best of both worlds: @@ -27,7 +27,7 @@ The Clever Kubernetes Operator was born from a concrete need: to allow users to While Kubernetes excels at deploying applications, databases come with operational constraints (backup, replication, restoration, updates…) that make self-management challenging. With the operator, you can maintain the comfort of a managed service while keeping control in your Kubernetes manifests. -### Supported Services +## Supported Services Originally designed for managed databases, the Clever Kubernetes Operator has expanded to support a wide range of Clever Cloud services as native Kubernetes Custom Resources: @@ -52,7 +52,7 @@ Originally designed for managed databases, the Clever Kubernetes Operator has ex - Keycloak - And more services to come -### Key Features +## Key Features The Clever Kubernetes Operator provides: @@ -68,7 +68,6 @@ This documentation guides you through: - Installing and configuring the Clever Kubernetes Operator in your cluster. - Managing Clever Cloud resources such as PostgreSQL and Redis through examples. - ## Prerequisites Before you begin, ensure that you have the following tools and resources based on your intended actions: @@ -82,7 +81,7 @@ Before you begin, ensure that you have the following tools and resources based o ### To Deploy the Operator - **Kubernetes Cluster:** Ensure you have access to a running Kubernetes cluster. -- **Kubectl:** Install Kubernetes command-line tool for managing cluster resources Installation guide available at https://kubernetes.io/docs/tasks/tools/. +- **Kubectl:** Install Kubernetes command-line tool for managing cluster resources Installation guide available at . - **Clever Cloud Account Credentials:** [Obtain API tokens and secrets from your Clever Cloud](/api/howto) account to configure the operator. These prerequisites are essential for getting started with the Clever Kubernetes Operator, whether you're contributing to its development or deploying it in production. @@ -113,11 +112,11 @@ consumer-secret = "your-consumer-secret" ``` **Best for:** + - Production environments - Full control over permissions - Fine-grained access scope configuration - > #### Need an easier way to get these credentials? > > Yes, the OAuth dance can be complex. For a simpler setup, there is a small application that you can deploy on Clever Cloud that automates most of the process for you. @@ -137,6 +136,7 @@ secret = "your-secret" If you already use the Clever Cloud CLI (clever-tools), you can extract these credentials from its configuration file, typically located at `$HOME/.config/clever-cloud/clever-tools.json`. The token and secret values can be found in this file and reused for the operator configuration. **Best for:** + - Standard usage scenarios - Development, testing, or demos - Users already using Clever Cloud CLI @@ -153,11 +153,11 @@ token = "your-bearer-token" ``` **Best for:** + - Simple integrations - Quick scripts or automation - Environments using the "oauthless" backend authentication - ## Installation The Clever Kubernetes Operator can be deployed in several ways depending on your needs. @@ -211,7 +211,7 @@ Replace ``, ``, ``, and ` kubectl logs -n clever-kubernetes-operator @@ -583,16 +582,19 @@ If you encounter issues with the Clever Kubernetes Operator, here are some commo **Possible Solutions:** 1. Check the operator logs for errors: + ```bash kubectl logs -n clever-kubernetes-operator ``` 2. Verify the custom resource is valid: + ```bash kubectl get -o yaml ``` 3. Ensure the CRDs are properly installed: + ```bash kubectl get crds | grep clevercloud ``` diff --git a/content/doc/marketplace.md b/content/doc/marketplace.md index 501218d8f..65f701e7b 100644 --- a/content/doc/marketplace.md +++ b/content/doc/marketplace.md @@ -24,9 +24,9 @@ Clever Cloud allows its Marketplace partners to provide services as add-ons with You want to help us to offer more services to our users? [Contact us](https://partners.clever-cloud.com). Then, you will be able to use our dedicated tools and APIs to provide your service as an add-on, whether they're hosted on Clever Cloud or not. -* [Add-on Manifest](#add-on-manifest): to provide your service as an add-on -* [Add-on provider requests](#add-on-provider-requests): to provision, modify or delete add-ons -* [Add-on infos API](#add-on-infos-api): to list provisioned add-ons and get detailed information about them +- [Add-on Manifest](#add-on-manifest): to provide your service as an add-on +- [Add-on provider requests](#add-on-provider-requests): to provision, modify or delete add-ons +- [Add-on infos API](#add-on-infos-api): to list provisioned add-ons and get detailed information about them ## Add-on Manifest @@ -55,16 +55,16 @@ First, provide a JSON manifest file that describes your add-on: ### Available fields -* `id` - An ID for your add-on. All lower case, no spaces or punctuation. Underscores and dashes are allowed. This can’t be changed after the first push. It is also used for HTTP basic auth when making provisioning calls. -* `name` (Optional) - A human-readable name for your add-on. You will be able to change it later in the dashboard, so you don't even have to provide it right now. -* `api/config_vars` - A list of configuration variables that will be returned on provisioning calls. Each `config_var` name must start with the capitalized, add-on id with underscores, as in the example. -* `api/password` - Password that Clever Cloud will send in HTTP basic auth when making provisioning calls. You should generate a long random string for that field. -* `api/sso_salt` - Shared secret used in single sign-on between the Clever Cloud admin panel and your service’s admin panel. You should generate a long random string for that field. -* `api/regions` - The list of geographical zones supported by your add-on. It cannot be empty. As for now, it *MUST* contain the element "eu". More will be supported. -* `api/production/base_url` - The production endpoint on which Clever Cloud sends actions requests (provision and deprovision). -* `api/production/sso_url` - The production endpoint for single sign-on. -* `api/test/base_url` - The test endpoint on which Clever Cloud sends actions requests. Used to test your service when you create an add-on provider. After the add-on creation,`api/production/base_url` is used. -* `api/test/sso_url` - The test endpoint for single sign-on. Used to test your service when you create an add-on provider. After that, the `api/production/sso_url` is used. +- `id` - An ID for your add-on. All lower case, no spaces or punctuation. Underscores and dashes are allowed. This can’t be changed after the first push. It is also used for HTTP basic auth when making provisioning calls. +- `name` (Optional) - A human-readable name for your add-on. You will be able to change it later in the dashboard, so you don't even have to provide it right now. +- `api/config_vars` - A list of configuration variables that will be returned on provisioning calls. Each `config_var` name must start with the capitalized, add-on id with underscores, as in the example. +- `api/password` - Password that Clever Cloud will send in HTTP basic auth when making provisioning calls. You should generate a long random string for that field. +- `api/sso_salt` - Shared secret used in single sign-on between the Clever Cloud admin panel and your service’s admin panel. You should generate a long random string for that field. +- `api/regions` - The list of geographical zones supported by your add-on. It cannot be empty. As for now, it *MUST* contain the element "eu". More will be supported. +- `api/production/base_url` - The production endpoint on which Clever Cloud sends actions requests (provision and deprovision). +- `api/production/sso_url` - The production endpoint for single sign-on. +- `api/test/base_url` - The test endpoint on which Clever Cloud sends actions requests. Used to test your service when you create an add-on provider. After the add-on creation,`api/production/base_url` is used. +- `api/test/sso_url` - The test endpoint for single sign-on. Used to test your service when you create an add-on provider. After that, the `api/production/sso_url` is used. ## Add-on Provider requests @@ -99,22 +99,22 @@ Response Body: { The request body contains the following fields: -* `addon_id` - The id we give to your add-on to identify it on our side. -* `owner_id` - The id of the customer this add-on will belong to. -* `owner_name` - The name of the customer. (Actually, the name of the organisation) -* `user_id` - The id of the user that is performing the action of provisioning this +- `addon_id` - The id we give to your add-on to identify it on our side. +- `owner_id` - The id of the customer this add-on will belong to. +- `owner_name` - The name of the customer. (Actually, the name of the organisation) +- `user_id` - The id of the user that is performing the action of provisioning this add-on. (The user will do it for the account of `owner_id`). -* `plan` - The slug field for the plan the user chose. You can create +- `plan` - The slug field for the plan the user chose. You can create plans in the dashboard once your add-on manifest has been uploaded to the Clever Cloud platform. We send you the slug of the given plan, not its name. -* `region` - The region to provision the add-on. As for now, only "EU" will be sent. -* `callback_url` - The URL you can use to get details about the add-on and the user. This URL is available as soon as the provisioning is done. You can't use this URL during the POST call. -* `options` - String -> String map with options. +- `region` - The region to provision the add-on. As for now, only "EU" will be sent. +- `callback_url` - The URL you can use to get details about the add-on and the user. This URL is available as soon as the provisioning is done. You can't use this URL during the POST call. +- `options` - String -> String map with options. The response body contains the following fields: -* `id` - The add-on id as seen from your side. It *MUST* be a String. -* `config` (Optional) - A String -> String map with value for each config\_var defined in your manifest. A key that is not in your config\_vars will be ignored. -* `message` (Optional) - A creation message we will display in the dashboard. +- `id` - The add-on id as seen from your side. It *MUST* be a String. +- `config` (Optional) - A String -> String map with value for each config\_var defined in your manifest. A key that is not in your config\_vars will be ignored. +- `message` (Optional) - A creation message we will display in the dashboard. ### De-provisioning @@ -128,14 +128,14 @@ Request Body: none Response Status: 200 ``` -* `addon_id` - This is the same as the `id` field set in the response to the provisioning call. +- `addon_id` - This is the same as the `id` field set in the response to the provisioning call. ### Examples You can find templates for add-on providers in various languages on GitHub: -* [Node.js](https://github.com/Redsmin/passport-clevercloud) -* [Scala with Play! Framework 2](https://github.com/CleverCloud/addon-provider-template) +- [Node.js](https://github.com/Redsmin/passport-clevercloud) +- [Scala with Play! Framework 2](https://github.com/CleverCloud/addon-provider-template) ## Add-on Infos API @@ -166,15 +166,15 @@ Response Body: [ ] ``` -* `provider_id` - Should be the same as the "id" field of your uploaded manifest. +- `provider_id` - Should be the same as the "id" field of your uploaded manifest. -* `addon_id` - The add-on's id from Clever Cloud's POV. +- `addon_id` - The add-on's id from Clever Cloud's POV. -* `callback_url` - URL to call to get more details about this add-on. +- `callback_url` - URL to call to get more details about this add-on. -* `plan` - The current plan of this add-on. +- `plan` - The current plan of this add-on. -* `owner_id` - The id of the owner that provisioned the add-on. This should never change. +- `owner_id` - The id of the owner that provisioned the add-on. This should never change. ### Get information about a specific add-on @@ -199,23 +199,23 @@ Response Body: { This endpoint gives you more information about a provisioned add-on. -* `id` - The add-on id from Clever Cloud's POV. +- `id` - The add-on id from Clever Cloud's POV. -* `name` - The name the user gave to this add-on in the Clever Cloud dashboard. +- `name` - The name the user gave to this add-on in the Clever Cloud dashboard. -* `config` - Configuration variables as you defined during the provision call. +- `config` - Configuration variables as you defined during the provision call. -* `callback_url` - The URL you just called. +- `callback_url` - The URL you just called. -* `owner_email` - One of the owner's email address. +- `owner_email` - One of the owner's email address. -* `owner_emails` - All the owner's email addresses. +- `owner_emails` - All the owner's email addresses. -* `owner_id` - The id of the owner that provisioned the add-on. This should never change. +- `owner_id` - The id of the owner that provisioned the add-on. This should never change. -* `region` - The region this add-on is located in. As for now, only "eu" is supported. +- `region` - The region this add-on is located in. As for now, only "eu" is supported. -* `domains` - Originally the domains names for the application owning the add-on. We return an empty list. +- `domains` - Originally the domains names for the application owning the add-on. We return an empty list. ### Update the configuration variables for an add-on @@ -339,13 +339,13 @@ Content-Type: application/x-www-form-urlencoded id=×tamp=&nav-data=&email=&user_id=&signature= ``` -* The hostname or `sso_url` comes from your add-on manifest -* The `id` is the ID for the previously provisioned resource -* The `timestamp` is a millisecond timestamp. You *SHOULD* verify that it's not older than a few minutes (like 5) -* The `user_id` is a unique string identifying the current user on the Clever Cloud platform -* The `email` is the current primary email of the current user on the Clever Cloud platform -* The `nav-data` contains information like the current app name and installed add-ons for Clever Cloud's Console. At the time of writing this doc, this field is always empty -* The `signature` is computed using the formula below +- The hostname or `sso_url` comes from your add-on manifest +- The `id` is the ID for the previously provisioned resource +- The `timestamp` is a millisecond timestamp. You *SHOULD* verify that it's not older than a few minutes (like 5) +- The `user_id` is a unique string identifying the current user on the Clever Cloud platform +- The `email` is the current primary email of the current user on the Clever Cloud platform +- The `nav-data` contains information like the current app name and installed add-ons for Clever Cloud's Console. At the time of writing this doc, this field is always empty +- The `signature` is computed using the formula below ### Token diff --git a/content/doc/metrics/_index.md b/content/doc/metrics/_index.md index ded3915a6..8b13fcfec 100644 --- a/content/doc/metrics/_index.md +++ b/content/doc/metrics/_index.md @@ -55,7 +55,7 @@ For example, check [this tutorial on how to create Slack alerts with Grafana](ht ### Templated dashboards in Grafana -Pre-configured dashboards are available in your `Clever Cloud` directory to help you visualize your application metrics. +Pre-configured dashboards are available in your `Clever Cloud` directory to help you visualize your application metrics. To customize a template and preserve your changes, create and save a **copy** of the dashboard in a **different** directory. Original templates may be overwritten when the Clever Cloud team releases updates. @@ -100,69 +100,69 @@ Telegraf provide lots of metrics described in their [documentation](https://gith Below, the list of all Warp 10 classes representing Telegraf metrics : -| Metric | Metric | -| ----------------------------------- | ----------------------------------- | -| conntrack.ip_conntrack_count | mem.swap_free | -| conntrack.ip_conntrack_max | mem.swap_total | -| cpu.usage_guest | mem.total | -| cpu.usage_guest_nice | mem.used | -| cpu.usage_idle | mem.used_percent | -| cpu.usage_iowait | mem.vmalloc_chunk | -| cpu.usage_irq | mem.vmalloc_total | -| cpu.usage_nice | mem.vmalloc_used | -| cpu.usage_softirq | mem.wired | -| cpu.usage_steal | mem.write_back | -| cpu.usage_system | mem.write_back_tmp | -| cpu.usage_user | net.bytes_recv | -| disk.free | net.bytes_sent | -| disk.inodes_free | net.drop_in | -| disk.inodes_total | net.drop_out | -| disk.inodes_used | net.err_in | -| disk.total | net.err_out | -| disk.used | net.packets_recv | -| disk.used_percent | net.packets_sent | -| http_response.http_response_code | net_response.response_time | -| http_response.response_time | net_response.result_code | -| http_response.result_code | net_response.result_type | -| http_response.result_type | netstat.tcp_close | -| kernel.boot_time | netstat.tcp_close_wait | -| kernel.context_switches | netstat.tcp_closing | -| kernel.entropy_avail | netstat.tcp_established | -| kernel.interrupts | netstat.tcp_fin_wait1 | -| kernel.processes_forked | netstat.tcp_fin_wait2 | -| mem.active | netstat.tcp_last_ack | -| mem.available | netstat.tcp_listen | -| mem.available_percent | netstat.tcp_none | -| mem.buffered | netstat.tcp_syn_recv | -| mem.cached | netstat.tcp_syn_sent | -| mem.commit_limit | netstat.tcp_time_wait | -| mem.committed_as | netstat.udp_socket | -| mem.dirty | processes.blocked | -| mem.free | processes.dead | -| mem.high_free | processes.idle | -| mem.high_total | processes.paging | -| mem.huge_page_size | processes.running | -| mem.huge_pages_free | processes.sleeping | -| mem.huge_pages_total | processes.stopped | -| mem.inactive | processes.total | -| mem.low_free | processes.total_threads | -| mem.low_total | processes.unknown | -| mem.mapped | processes.zombies | -| mem.page_tables | procstat_lookup.pid_count | -| mem.shared | system.load1 | -| mem.slab | system.load1_per_cpu | -| mem.swap_cached | jvm.statsd-jvm-profiler_heap_ps-old-gen_max.value | -| jvm.statsd-jvm-profiler_pending-finalization-count.value | jvm.statsd-jvm-profiler_nonheap_total_committed.value | -| jvm.statsd-jvm-profiler_loaded-class-count.value | jvm.metrics_jvm_heapMemoryUsage_used.value | -| jvm.statsd-jvm-profiler_gc_PS_Scavenge_count.value | jvm.metrics_jvm_nonHeapMemoryUsage_used.value | -| jvm.statsd-jvm-profiler_nonheap_metaspace_init.value | jvm.statsd-jvm-profiler_nonheap_total_used.value | -| jvm.statsd-jvm-profiler_heap_ps-survivor-space_used.value | jvm.statsd-jvm-profiler_heap_ps-eden-space_init.value | -| jvm.statsd-jvm-profiler_gc_PS_MarkSweep_time.value | jvm.statsd-jvm-profiler_nonheap_total_max.value | -| jvm.statsd-jvm-profiler_heap_ps-eden-space_max.value | jvm.statsd-jvm-profiler_nonheap_compressed-class-space_max.value | -| jvm.statsd-jvm-profiler_heap_total_init.value | jvm.statsd-jvm-profiler_nonheap_code-cache_used.value | -| jvm.statsd-jvm-profiler_nonheap_metaspace_used.value | jvm.statsd-jvm-profiler_nonheap_compressed-class-space_init.value | -| jvm.statsd-jvm-profiler_nonheap_metaspace_max.value | jvm.statsd-jvm-profiler_gc_PS_MarkSweep_count.value | -| jvm.statsd-jvm-profiler_heap_ps-eden-space_used.value | | +| Metric | Metric | +| --------------------------------------------------------- | ----------------------------------------------------------------- | +| conntrack.ip_conntrack_count | mem.swap_free | +| conntrack.ip_conntrack_max | mem.swap_total | +| cpu.usage_guest | mem.total | +| cpu.usage_guest_nice | mem.used | +| cpu.usage_idle | mem.used_percent | +| cpu.usage_iowait | mem.vmalloc_chunk | +| cpu.usage_irq | mem.vmalloc_total | +| cpu.usage_nice | mem.vmalloc_used | +| cpu.usage_softirq | mem.wired | +| cpu.usage_steal | mem.write_back | +| cpu.usage_system | mem.write_back_tmp | +| cpu.usage_user | net.bytes_recv | +| disk.free | net.bytes_sent | +| disk.inodes_free | net.drop_in | +| disk.inodes_total | net.drop_out | +| disk.inodes_used | net.err_in | +| disk.total | net.err_out | +| disk.used | net.packets_recv | +| disk.used_percent | net.packets_sent | +| http_response.http_response_code | net_response.response_time | +| http_response.response_time | net_response.result_code | +| http_response.result_code | net_response.result_type | +| http_response.result_type | netstat.tcp_close | +| kernel.boot_time | netstat.tcp_close_wait | +| kernel.context_switches | netstat.tcp_closing | +| kernel.entropy_avail | netstat.tcp_established | +| kernel.interrupts | netstat.tcp_fin_wait1 | +| kernel.processes_forked | netstat.tcp_fin_wait2 | +| mem.active | netstat.tcp_last_ack | +| mem.available | netstat.tcp_listen | +| mem.available_percent | netstat.tcp_none | +| mem.buffered | netstat.tcp_syn_recv | +| mem.cached | netstat.tcp_syn_sent | +| mem.commit_limit | netstat.tcp_time_wait | +| mem.committed_as | netstat.udp_socket | +| mem.dirty | processes.blocked | +| mem.free | processes.dead | +| mem.high_free | processes.idle | +| mem.high_total | processes.paging | +| mem.huge_page_size | processes.running | +| mem.huge_pages_free | processes.sleeping | +| mem.huge_pages_total | processes.stopped | +| mem.inactive | processes.total | +| mem.low_free | processes.total_threads | +| mem.low_total | processes.unknown | +| mem.mapped | processes.zombies | +| mem.page_tables | procstat_lookup.pid_count | +| mem.shared | system.load1 | +| mem.slab | system.load1_per_cpu | +| mem.swap_cached | jvm.statsd-jvm-profiler_heap_ps-old-gen_max.value | +| jvm.statsd-jvm-profiler_pending-finalization-count.value | jvm.statsd-jvm-profiler_nonheap_total_committed.value | +| jvm.statsd-jvm-profiler_loaded-class-count.value | jvm.metrics_jvm_heapMemoryUsage_used.value | +| jvm.statsd-jvm-profiler_gc_PS_Scavenge_count.value | jvm.metrics_jvm_nonHeapMemoryUsage_used.value | +| jvm.statsd-jvm-profiler_nonheap_metaspace_init.value | jvm.statsd-jvm-profiler_nonheap_total_used.value | +| jvm.statsd-jvm-profiler_heap_ps-survivor-space_used.value | jvm.statsd-jvm-profiler_heap_ps-eden-space_init.value | +| jvm.statsd-jvm-profiler_gc_PS_MarkSweep_time.value | jvm.statsd-jvm-profiler_nonheap_total_max.value | +| jvm.statsd-jvm-profiler_heap_ps-eden-space_max.value | jvm.statsd-jvm-profiler_nonheap_compressed-class-space_max.value | +| jvm.statsd-jvm-profiler_heap_total_init.value | jvm.statsd-jvm-profiler_nonheap_code-cache_used.value | +| jvm.statsd-jvm-profiler_nonheap_metaspace_used.value | jvm.statsd-jvm-profiler_nonheap_compressed-class-space_init.value | +| jvm.statsd-jvm-profiler_nonheap_metaspace_max.value | jvm.statsd-jvm-profiler_gc_PS_MarkSweep_count.value | +| jvm.statsd-jvm-profiler_heap_ps-eden-space_used.value | | ### Examples and usages diff --git a/content/doc/metrics/new-relic.md b/content/doc/metrics/new-relic.md index 9971c087b..c035f4e87 100644 --- a/content/doc/metrics/new-relic.md +++ b/content/doc/metrics/new-relic.md @@ -67,13 +67,13 @@ If the `appname` or `NEW_RELIC_APP_NAME` is not specified, we use your applicati If you need to fine-tune agent settings, you can use the following environment variables: -| Variable | -|-------------------- -|[`CC_NEWRELIC_BROWSER_MONITORING_AUTO_INSTRUMENT`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-autorum) | -|[`CC_NEWRELIC_DISTRIBUTED_TRACING_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-distributed-enabled) | -|[`CC_NEWRELIC_ERROR_COLLECTOR_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-err-enabled) | -|[`CC_NEWRELIC_TRANSACTION_TRACER_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-enable) | -|[`CC_NEWRELIC_TRANSACTION_TRACER_RECORD_SQL`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-sql) | +| Variable | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [`CC_NEWRELIC_BROWSER_MONITORING_AUTO_INSTRUMENT`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-autorum) | +| [`CC_NEWRELIC_DISTRIBUTED_TRACING_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-distributed-enabled) | +| [`CC_NEWRELIC_ERROR_COLLECTOR_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-err-enabled) | +| [`CC_NEWRELIC_TRANSACTION_TRACER_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-enable) | +| [`CC_NEWRELIC_TRANSACTION_TRACER_RECORD_SQL`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-sql) | ### Usage diff --git a/content/doc/metrics/warp10.md b/content/doc/metrics/warp10.md index 0d8afa0ce..33bd9255c 100644 --- a/content/doc/metrics/warp10.md +++ b/content/doc/metrics/warp10.md @@ -24,13 +24,14 @@ The Clever Cloud Warp 10 endpoint is: https://c2-warp10-clevercloud-customers.services.clever-cloud.com/api/v0 ``` -* Learn more about [endpoint gateway on warp10.io](https://www.warp10.io/content/03_Documentation/03_Interacting_with_Warp_10/01_Introduction) +- Learn more about [endpoint gateway on warp10.io](https://www.warp10.io/content/03_Documentation/03_Interacting_with_Warp_10/01_Introduction) You can find the endpoint and an available token under the `metric` tab of your application. You can query our Warp 10 platform with your own script. Here's an example with `curl`: ```bash curl -T https://c2-warp10-clevercloud-customers.services.clever-cloud.com/api/v0/exec ``` + ## Token Tokens are based on your application with the notion of producer and owner. Hence, only the data owner can see it. diff --git a/content/doc/quickstart.md b/content/doc/quickstart.md index cafd38ef7..0b14c6cb5 100644 --- a/content/doc/quickstart.md +++ b/content/doc/quickstart.md @@ -44,8 +44,8 @@ There are two ways to sign up for Clever Cloud: **email** or **GitHub login**. The GitHub sign up allows you to create an account or link your existing one to GitHub, in one click. This process asks the following permissions: -* Read your Public Key -* Read User Repositories +- Read your Public Key +- Read User Repositories The "repository permission" is used to deploy your GitHub apps directly to Clever Cloud, with a simple step. If you need to give access to Clever Cloud's API to a specific GitHub organisation, you can [do it here](https://github.com/settings/connections/applications/d96bd8fd996d2ca783cc). @@ -67,10 +67,10 @@ Please, backup your recovery codes, we won't be able to restore access to your a An application is defined on Clever Cloud by the following elements: -* a dedicated language/framework; -* a deployment method (FTP and/or Git); -* resources consumption (CPU, RAM, Disk…), depending on the language or framework used; -* an optional configuration file you may add to your project. +- a dedicated language/framework; +- a deployment method (FTP and/or Git); +- resources consumption (CPU, RAM, Disk…), depending on the language or framework used; +- an optional configuration file you may add to your project. If one of these elements is missing, Clever Cloud can't deploy your application properly (except the configuration file, optional in some cases). @@ -145,8 +145,8 @@ Enter the name and the description of your application. #### Optional steps -* The wizard will offer you to [add an add-on](/doc/addons) to your application -* The wizard will offer you to [add environment variables](/doc/develop/env-variables) to your application +- The wizard will offer you to [add an add-on](/doc/addons) to your application +- The wizard will offer you to [add environment variables](/doc/develop/env-variables) to your application {{% /steps %}} @@ -232,9 +232,9 @@ The full tutorial about adding SSH key is here: [adding SSH keys](/doc/account/s {{% details title= "Git ⋅ Unable to resolve the reference master" closed="true" %}} You are probably trying to push from another branch. Keep in mind that: -* You can only push to the **master** branch for deployment. Trying to push to another branch will trigger an error. -* You cannot push a tag (which refers to a commit) to the remote repository. If you do so, **no deployment** will be triggered. -* In order to push to **master** from a non-master local branch, use this syntax: +- You can only push to the **master** branch for deployment. Trying to push to another branch will trigger an error. +- You cannot push a tag (which refers to a commit) to the remote repository. If you do so, **no deployment** will be triggered. +- In order to push to **master** from a non-master local branch, use this syntax: ```bash git push :master @@ -262,16 +262,16 @@ If you have to push directly to a repository to deploy an application (eg if you There are many tabs available in the application's menu on Clever Console: -* **Information:** General information about your application -* **Scalability:** Set-up scalability options -* **Domain names:** Manage custom domain names -* **Environment variables:** Manage environment variables -* **Service dependencies:** Link add-ons and applications -* **Exposed configuration:** Manage exposed environment variables -* **Activity:** Track last deployments -* **Logs:** Visualize application's logs -* **Metrics:** Visualize application's metrics -* **Consumption:** Visualize your application's consumption. +- **Information:** General information about your application +- **Scalability:** Set-up scalability options +- **Domain names:** Manage custom domain names +- **Environment variables:** Manage environment variables +- **Service dependencies:** Link add-ons and applications +- **Exposed configuration:** Manage exposed environment variables +- **Activity:** Track last deployments +- **Logs:** Visualize application's logs +- **Metrics:** Visualize application's metrics +- **Consumption:** Visualize your application's consumption. ### Create your first add-on @@ -351,8 +351,8 @@ Clever Cloud provides multiple add-ons to work with your applications: There are two kinds of billing: -* Per-month billing: Add-ons with fixed resources (storage, CPU and RAM) -* Per-usage billing: Add-ons based on consumption, like [FS Bucket](/doc/addons/fs-bucket) and [Cellar](/doc/addons/cellar) +- Per-month billing: Add-ons with fixed resources (storage, CPU and RAM) +- Per-usage billing: Add-ons based on consumption, like [FS Bucket](/doc/addons/fs-bucket) and [Cellar](/doc/addons/cellar) {{< callout type="warning" >}} **Free Plan:** add-ons having a free plan are meant for testing purposes, not production usage. These add-ons usually rely on shared resources, resulting in variable, non-guaranteed performances and stability. Shared clusters may not be running the same version as dedicated instances. @@ -366,11 +366,11 @@ There are two kinds of billing: Once an add-on is created, at least two tabs are available in the Clever Cloud console: -* **Add-on dashboard:** This screen provides and overview of your add-on and its options, depending on the type of add-on it is. +- **Add-on dashboard:** This screen provides and overview of your add-on and its options, depending on the type of add-on it is. !["Add-on dashboard"](/images/addon-dashboard.png "Example of an add-on dashboard") -* **Information tab:** This screen sums-up the characteristics of the selected add-on. +- **Information tab:** This screen sums-up the characteristics of the selected add-on. The system shows features and environment variables (if applicable). Other tabs may be available, depending on the add-on type. diff --git a/content/doc/reference/cli.md b/content/doc/reference/cli.md index f296e8ee3..90216fec8 100644 --- a/content/doc/reference/cli.md +++ b/content/doc/reference/cli.md @@ -19,10 +19,13 @@ aliases: - /reference/clever-tools/getting_started --- + + This document is automatically generated from Clever Tools and Clever Cloud API. It covers all Clever Tools commands and options. Use it to better understand this CLI and its capabilities or to train/use LLMs, AI-assisted IDEs. To use Clever Tools, you need: -- A Clever Cloud account, create one at https://console.clever-cloud.com/ + +- A Clever Cloud account, create one at - The Clever Tools CLI installed, see installation instructions below In CI/CD pipelines or for one-off usage, you can use it through `npx` or `npm exec`: @@ -51,7 +54,7 @@ Clever Cloud CLI is based on Node.js. We thought it to be easily available on an If you use Arch Linux, install packages [from AUR](https://aur.archlinux.org/packages/clever-tools-bin/). If you don't know how to use this, run: -``` +```console git clone https://aur.archlinux.org/clever-tools-bin.git clever-tools cd clever-tools makepkg -si @@ -61,7 +64,7 @@ makepkg -si If you use a GNU/Linux distribution that uses `.rpm` packages like CentOS or Fedora, run: -``` +```console curl -s https://clever-tools.clever-cloud.com/repos/cc-nexus-rpm.repo > /etc/yum.repos.d/cc-nexus-rpm.repo yum update yum install clever-tools @@ -74,7 +77,7 @@ yum install clever-tools If you use a GNU/Linux distribution that uses `.deb` packages like Debian or Ubuntu, run: -``` +```console curl -fsSL https://clever-tools.clever-cloud.com/gpg/cc-nexus-deb.public.gpg.key | gpg --dearmor -o /usr/share/keyrings/cc-nexus-deb.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/cc-nexus-deb.gpg] https://nexus.clever-cloud.com/repository/deb stable main" | tee -a /etc/apt/sources.list apt update @@ -89,7 +92,7 @@ apt install clever-tools If you are using Exherbo, run: -``` +```console cave resolve repository/CleverCloud -zx1 cave resolve clever-tools-bin -zx ``` @@ -98,7 +101,7 @@ cave resolve clever-tools-bin -zx If you use another GNU/Linux distribution, download the `.tar.gz` archive and extract the binary in your `PATH`: -``` +```console curl -O https://clever-tools.clever-cloud.com/releases/latest/clever-tools-latest_linux.tar.gz tar xvzf clever-tools-latest_linux.tar.gz cp clever-tools-latest_linux/clever ~/.local/bin/ @@ -106,7 +109,7 @@ cp clever-tools-latest_linux/clever ~/.local/bin/ > [!TIP] > The packages are available on Clever Cloud's Cellar bucket: [clever-tools-latest_linux.tar.gz](https://clever-tools.clever-cloud.com/releases/latest/clever-tools-latest_linux.tar.gz). \ -> Retrieve any release by replacing `latest` (path and filename) with the version number you need. +> Retrieve any release by replacing `latest` (path and filename) with the version number you need. ### macOS @@ -116,7 +119,7 @@ We only provide macOS packages for Apple Silicon processors. On an Intel Mac, pr If you use macOS and you have [Homebrew](https://brew.sh) installed, run: -``` +```console brew install CleverCloud/homebrew-tap/clever-tools ``` @@ -124,7 +127,7 @@ brew install CleverCloud/homebrew-tap/clever-tools If you use macOS, but you don't have [Homebrew](https://brew.sh) installed, download the `.tar.gz` archive and extract the binary in your `PATH`: -``` +```console curl -O https://clever-tools.clever-cloud.com/releases/latest/clever-tools-latest_macos.tar.gz tar xvzf clever-tools-latest_macos.tar.gz cp clever-tools-latest_macos/clever ~/.local/bin/ @@ -140,7 +143,7 @@ cp clever-tools-latest_macos/clever ~/.local/bin/ If you use Windows run in a terminal: -``` +```console winget install CleverTools ``` @@ -160,9 +163,9 @@ $env:PATH += ";$(Resolve-Path .\clever-tools-latest_win\)" ### Docker -If you are using docker, use the image provided [here](https://hub.docker.com/r/clevercloud/clever-tools/). +If you are using Docker, use the [Clever Tools image from Docker Hub](https://hub.docker.com/r/clevercloud/clever-tools/). -``` +```console docker pull clevercloud/clever-tools docker run --rm clever-tools ``` @@ -268,12 +271,14 @@ Default deployment zone is `par`, default plan is the lowest available. **Since:** 2.1.0 **Usage** -``` + +```console clever accesslogs [options] ``` **Options** -``` + +```console --addon Add-on ID or real ID --after, --since Fetch logs after this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h) -a, --alias Short name for the application @@ -289,12 +294,14 @@ clever accesslogs [options] **Since:** 0.2.3 **Usage** -``` + +```console clever activity [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -f, --follow Track new deployments in activity list @@ -309,12 +316,14 @@ clever activity [options] **Since:** 0.2.3 **Usage** -``` + +```console clever addon [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -326,18 +335,21 @@ clever addon [options] **Since:** 0.2.3 **Usage** -``` + +```console clever addon create [options] ``` **Arguments** -``` + +```console addon-provider Add-on provider addon-name Add-on name ``` **Options** -``` + +```console --addon-version The version to use for the add-on -F, --format Output format (human, json) (default: human) -l, --link Link the created add-on to the app with the specified alias @@ -355,17 +367,20 @@ addon-name Add-on name **Since:** 0.2.3 **Usage** -``` + +```console clever addon delete [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) -y, --yes Skip confirmation prompts ``` @@ -377,17 +392,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 2.11.0 **Usage** -``` + +```console clever addon env [options] ``` **Arguments** -``` + +```console addon-id Add-on ID or real ID ``` **Options** -``` + +```console -F, --format Output format (human, json, shell) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) (deprecated, organisation is now resolved automatically) ``` @@ -399,12 +417,14 @@ addon-id Add-on ID or real ID **Since:** 0.2.3 **Usage** -``` + +```console clever addon list [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -416,12 +436,14 @@ clever addon list [options] **Since:** 0.2.3 **Usage** -``` + +```console clever addon providers [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -433,17 +455,20 @@ clever addon providers [options] **Since:** 0.2.3 **Usage** -``` + +```console clever addon providers show [options] ``` **Arguments** -``` + +```console addon-provider Add-on provider ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -455,18 +480,21 @@ addon-provider Add-on provider **Since:** 0.3.0 **Usage** -``` + +```console clever addon rename [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) addon-name Add-on name ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -477,12 +505,14 @@ addon-name Add-on name **Since:** 0.3.0 **Usage** -``` + +```console clever applications [options] ``` **Options** -``` + +```console -j, --json Show result in JSON format --only-aliases List only application aliases ``` @@ -494,12 +524,14 @@ clever applications [options] **Since:** 3.8.0 **Usage** -``` + +```console clever applications list [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -511,12 +543,14 @@ clever applications list [options] **Since:** 0.2.0 **Usage** -``` + +```console clever cancel-deploy [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -528,12 +562,14 @@ clever cancel-deploy [options] **Since:** 2.5.0 **Usage** -``` + +```console clever config [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -545,17 +581,20 @@ clever config [options] **Since:** 2.5.0 **Usage** -``` + +```console clever config get [options] ``` **Arguments** -``` + +```console configuration-name Configuration to manage: name, description, zero-downtime, sticky-sessions, cancel-on-push, force-https, or task ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -567,18 +606,21 @@ configuration-name Configuration to manage: name, description, zero- **Since:** 2.5.0 **Usage** -``` + +```console clever config set [options] ``` **Arguments** -``` + +```console configuration-name Configuration to manage: name, description, zero-downtime, sticky-sessions, cancel-on-push, force-https, or task configuration-value The new value of the configuration ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -590,12 +632,14 @@ configuration-value The new value of the configuration **Since:** 2.5.0 **Usage** -``` + +```console clever config update [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) --description Set application description @@ -619,7 +663,8 @@ clever config update [options] **Since:** 4.6.0 **Usage** -``` + +```console clever config-provider ``` @@ -630,17 +675,20 @@ clever config-provider **Since:** 4.6.0 **Usage** -``` + +```console clever config-provider get [options] ``` **Arguments** -``` + +```console addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or name (if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json, shell) (default: human) ``` @@ -652,17 +700,20 @@ addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or nam **Since:** 4.6.0 **Usage** -``` + +```console clever config-provider import [options] ``` **Arguments** -``` + +```console addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or name (if unambiguous) ``` **Options** -``` + +```console -F, --format Input format (name-equals-value, json) (default: name-equals-value) ``` @@ -673,12 +724,14 @@ addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or nam **Since:** 4.6.0 **Usage** -``` + +```console clever config-provider list [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -689,12 +742,14 @@ clever config-provider list [options] **Since:** 4.6.0 **Usage** -``` + +```console clever config-provider open ``` **Arguments** -``` + +```console addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or name (if unambiguous) ``` @@ -705,29 +760,33 @@ addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or nam **Since:** 4.6.0 **Usage** -``` + +```console clever config-provider rm ``` **Arguments** -``` + +```console addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or name (if unambiguous) variable-name Name of the environment variable ``` ### config-provider set -**Description:** Add or update an environment variable named with the value +**Description:** Add or update an environment variable named `` with the value `` **Since:** 4.6.0 **Usage** -``` + +```console clever config-provider set ``` **Arguments** -``` + +```console addon-id|config-provider-id|addon-name Add-on ID, real ID (config_xxx) or name (if unambiguous) variable-name Name of the environment variable variable-value Value of the environment variable @@ -740,12 +799,14 @@ variable-value Value of the environment variable **Since:** 1.0.0 **Usage** -``` + +```console clever console [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -757,17 +818,20 @@ clever console [options] **Since:** 0.2.0 **Usage** -``` + +```console clever create --type [] [options] ``` **Arguments** -``` + +```console app-name Application name (current directory name is used if not specified) (optional) ``` **Options** -``` + +```console -t, --type Instance type (required) -a, --alias Short name for the application -F, --format Output format (human, json) (default: human) @@ -784,7 +848,8 @@ app-name Application name (current directory name **Since:** 2.10.0 **Usage** -``` + +```console clever curl ``` @@ -795,7 +860,8 @@ clever curl **Since:** 2.10.0 **Usage** -``` + +```console clever database ``` @@ -806,17 +872,20 @@ clever database **Since:** 2.10.0 **Usage** -``` + +```console clever database backups [options] ``` **Arguments** -``` + +```console database-id|addon-id Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) (deprecated, organisation is now resolved automatically) ``` @@ -828,18 +897,21 @@ database-id|addon-id Any database ID (format: addon_UUID, pos **Since:** 2.10.0 **Usage** -``` + +```console clever database backups download [options] ``` **Arguments** -``` + +```console database-id|addon-id Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...) backup-id A Database backup ID (format: UUID) ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) (deprecated, organisation is now resolved automatically) --output, --out Redirect the output of the command in a file ``` @@ -851,12 +923,14 @@ backup-id A Database backup ID (format: UUID) **Since:** 0.7.0 **Usage** -``` + +```console clever delete [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -y, --yes Skip confirmation and delete the application directly @@ -869,12 +943,14 @@ clever delete [options] **Since:** 0.2.0 **Usage** -``` + +```console clever deploy [options] ``` **Options** -``` + +```console -a, --alias Short name for the application -b, --branch Branch to push (current branch by default) -e, --exit-on Step at which the logs streaming is ended (deploy-start, deploy-end, never) (default: deploy-end) @@ -892,12 +968,14 @@ clever deploy [options] **Since:** 1.6.0 **Usage** -``` + +```console clever diag [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -908,12 +986,14 @@ clever diag [options] **Since:** 0.2.0 **Usage** -``` + +```console clever domain [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -F, --format Output format (human, json) (default: human) @@ -926,17 +1006,20 @@ clever domain [options] **Since:** 0.2.0 **Usage** -``` + +```console clever domain add [options] ``` **Arguments** -``` + +```console fqdn Domain name of the application ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -948,12 +1031,14 @@ fqdn Domain name of the application **Since:** 3.9.0 **Usage** -``` + +```console clever domain diag [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) --filter Check only domains containing the provided text @@ -967,12 +1052,14 @@ clever domain diag [options] **Since:** 2.7.0 **Usage** -``` + +```console clever domain favourite [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -F, --format Output format (human, json) (default: human) @@ -985,17 +1072,20 @@ clever domain favourite [options] **Since:** 2.7.0 **Usage** -``` + +```console clever domain favourite set [options] ``` **Arguments** -``` + +```console fqdn Domain name of the application ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -1007,12 +1097,14 @@ fqdn Domain name of the application **Since:** 2.7.0 **Usage** -``` + +```console clever domain favourite unset [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -1024,12 +1116,14 @@ clever domain favourite unset [options] **Since:** 3.9.0 **Usage** -``` + +```console clever domain overview [options] ``` **Options** -``` + +```console --filter Get only domains containing the provided text -F, --format Output format (human, json) (default: human) ``` @@ -1041,17 +1135,20 @@ clever domain overview [options] **Since:** 0.2.0 **Usage** -``` + +```console clever domain rm [options] ``` **Arguments** -``` + +```console fqdn Domain name of the application ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -1063,12 +1160,14 @@ fqdn Domain name of the application **Since:** 0.9.0 **Usage** -``` + +```console clever drain [options] ``` **Options** -``` + +```console --addon Add-on ID or real ID -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -1082,17 +1181,20 @@ clever drain [options] **Since:** 4.11.0 **Usage** -``` + +```console clever drain check [options] ``` **Arguments** -``` + +```console drain-id Drain ID ``` **Options** -``` + +```console --addon Add-on ID or real ID -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -1106,18 +1208,21 @@ drain-id Drain ID **Since:** 0.9.0 **Usage** -``` + +```console clever drain create [options] ``` **Arguments** -``` + +```console drain-type Drain type (betterstack, datadog, elasticsearch, newrelic, ovh-tcp, raw-http, syslog-tcp, syslog-udp) drain-url Drain URL ``` **Options** -``` + +```console --addon Add-on ID or real ID -a, --alias Short name for the application -k, --api-key API key (for newrelic) @@ -1136,17 +1241,20 @@ drain-url Drain URL **Since:** 0.9.0 **Usage** -``` + +```console clever drain disable [options] ``` **Arguments** -``` + +```console drain-id Drain ID ``` **Options** -``` + +```console --addon Add-on ID or real ID -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -1159,17 +1267,20 @@ drain-id Drain ID **Since:** 0.9.0 **Usage** -``` + +```console clever drain enable [options] ``` **Arguments** -``` + +```console drain-id Drain ID ``` **Options** -``` + +```console --addon Add-on ID or real ID -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -1182,17 +1293,20 @@ drain-id Drain ID **Since:** 0.9.0 **Usage** -``` + +```console clever drain get [options] ``` **Arguments** -``` + +```console drain-id Drain ID ``` **Options** -``` + +```console --addon Add-on ID or real ID -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -1206,17 +1320,20 @@ drain-id Drain ID **Since:** 0.9.0 **Usage** -``` + +```console clever drain remove [options] ``` **Arguments** -``` + +```console drain-id Drain ID ``` **Options** -``` + +```console --addon Add-on ID or real ID -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -1229,12 +1346,14 @@ drain-id Drain ID **Since:** 3.13.0 **Usage** -``` + +```console clever emails [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -1245,12 +1364,14 @@ clever emails [options] **Since:** 3.13.0 **Usage** -``` + +```console clever emails add ``` **Arguments** -``` + +```console email Email address ``` @@ -1261,7 +1382,8 @@ email Email address **Since:** 3.13.0 **Usage** -``` + +```console clever emails open ``` @@ -1272,12 +1394,14 @@ clever emails open **Since:** 3.13.0 **Usage** -``` + +```console clever emails primary ``` **Arguments** -``` + +```console email Email address ``` @@ -1288,12 +1412,14 @@ email Email address **Since:** 3.13.0 **Usage** -``` + +```console clever emails remove ``` **Arguments** -``` + +```console email Email address ``` @@ -1304,12 +1430,14 @@ email Email address **Since:** 3.13.0 **Usage** -``` + +```console clever emails remove-all [options] ``` **Options** -``` + +```console -y, --yes Skip confirmation ``` @@ -1320,12 +1448,14 @@ clever emails remove-all [options] **Since:** 0.2.0 **Usage** -``` + +```console clever env [options] ``` **Options** -``` + +```console --add-export Display sourceable env variables setting (deprecated, use `--format shell` instead) -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -1340,12 +1470,14 @@ clever env [options] **Since:** 0.3.0 **Usage** -``` + +```console clever env import [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) --json Import variables as JSON (an array of { "name": "THE_NAME", "value": "THE_VALUE" } objects) @@ -1353,22 +1485,25 @@ clever env import [options] ### env import-vars -**Description:** Add or update environment variables named (comma-separated), taking their values from the current environment +**Description:** Add or update environment variables named `` (comma-separated), taking their values from the current environment **Since:** 2.0.0 **Usage** -``` + +```console clever env import-vars [options] ``` **Arguments** -``` + +```console variable-names Comma separated list of names of the environment variables ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -1380,40 +1515,46 @@ variable-names Comma separated list of names of the environment **Since:** 0.3.0 **Usage** -``` + +```console clever env rm [options] ``` **Arguments** -``` + +```console variable-name Name of the environment variable ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` ### env set -**Description:** Add or update an environment variable named with the value +**Description:** Add or update an environment variable named `` with the value `` **Since:** 0.3.0 **Usage** -``` + +```console clever env set [options] ``` **Arguments** -``` + +```console variable-name Name of the environment variable variable-value Value of the environment variable ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -1425,12 +1566,14 @@ variable-value Value of the environment variable **Since:** 3.11.0 **Usage** -``` + +```console clever features [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -1441,12 +1584,14 @@ clever features [options] **Since:** 3.11.0 **Usage** -``` + +```console clever features disable ``` **Arguments** -``` + +```console features Comma-separated list of experimental features to manage ``` @@ -1457,12 +1602,14 @@ features Comma-separated list of experimental features to manage **Since:** 3.11.0 **Usage** -``` + +```console clever features enable ``` **Arguments** -``` + +```console features Comma-separated list of experimental features to manage ``` @@ -1473,12 +1620,14 @@ features Comma-separated list of experimental features to manage **Since:** 3.11.0 **Usage** -``` + +```console clever features info ``` **Arguments** -``` + +```console feature Experimental feature to manage ``` @@ -1489,12 +1638,14 @@ feature Experimental feature to manage **Since:** 3.11.0 **Usage** -``` + +```console clever features list [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -1505,7 +1656,8 @@ clever features list [options] **Since:** 0.1.0 **Usage** -``` + +```console clever help ``` @@ -1516,7 +1668,8 @@ clever help **Since:** 4.3.0 **Usage** -``` + +```console clever k8s ``` @@ -1527,17 +1680,20 @@ clever k8s **Since:** 4.9.0 **Usage** -``` + +```console clever k8s activity [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) --limit Number of events to fetch (1 to 1000) (default: 50) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) @@ -1550,17 +1706,20 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.3.0 **Usage** -``` + +```console clever k8s add-persistent-storage [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1571,17 +1730,20 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.3.0 **Usage** -``` + +```console clever k8s create [options] ``` **Arguments** -``` + +```console cluster-name Kubernetes cluster name ``` **Options** -``` + +```console --autoscaling Enable the cluster autoscaler --cluster-version Kubernetes version to deploy (e.g.: 1.36) --description Free-form cluster description @@ -1603,17 +1765,20 @@ cluster-name Kubernetes cluster name **Since:** 4.3.0 **Usage** -``` + +```console clever k8s delete [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) -y, --yes Skip confirmation prompts ``` @@ -1625,17 +1790,20 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.3.0 **Usage** -``` + +```console clever k8s get [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1647,17 +1815,20 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.3.0 **Usage** -``` + +```console clever k8s get-kubeconfig [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1668,12 +1839,14 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.3.0 **Usage** -``` + +```console clever k8s list [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1685,7 +1858,8 @@ clever k8s list [options] **Since:** 4.9.0 **Usage** -``` + +```console clever k8s nodegroups ``` @@ -1696,19 +1870,22 @@ clever k8s nodegroups **Since:** 4.9.0 **Usage** -``` + +```console clever k8s nodegroups create [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name nodegroup-name Node group name (lowercase RFC 1123, max 63 chars) flavor:count Node group flavor and target node count (format: :, e.g.: XS:3) ``` **Options** -``` + +```console --autoscaling Enable cluster autoscaler for this node group (requires --min and --max) --description Free-form node group description --max Maximum node count when autoscaling is enabled @@ -1724,18 +1901,21 @@ flavor:count Node group flavor and target node count **Since:** 4.9.0 **Usage** -``` + +```console clever k8s nodegroups delete [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name nodegroup-id|nodegroup-name Kubernetes node group ID or name ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) -y, --yes Skip confirmation prompts ``` @@ -1747,18 +1927,21 @@ nodegroup-id|nodegroup-name Kubernetes node group ID or name **Since:** 4.9.0 **Usage** -``` + +```console clever k8s nodegroups get [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name nodegroup-id|nodegroup-name Kubernetes node group ID or name ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1770,17 +1953,20 @@ nodegroup-id|nodegroup-name Kubernetes node group ID or name **Since:** 4.9.0 **Usage** -``` + +```console clever k8s nodegroups list [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1792,18 +1978,21 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.9.0 **Usage** -``` + +```console clever k8s nodegroups update [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name nodegroup-id|nodegroup-name Kubernetes node group ID or name ``` **Options** -``` + +```console --autoscaling Enable the cluster autoscaler --count Target node count --description Free-form node group description @@ -1821,12 +2010,14 @@ nodegroup-id|nodegroup-name Kubernetes node group ID or name **Since:** 4.9.0 **Usage** -``` + +```console clever k8s quota [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1838,17 +2029,20 @@ clever k8s quota [options] **Since:** 4.9.0 **Usage** -``` + +```console clever k8s update [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console --autoscaling Enable the cluster autoscaler --description Free-form cluster description --disable-autoscaling Disable the cluster autoscaler @@ -1864,17 +2058,20 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.9.0 **Usage** -``` + +```console clever k8s version [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1886,17 +2083,20 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.9.0 **Usage** -``` + +```console clever k8s version check [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -1908,17 +2108,20 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 4.9.0 **Usage** -``` + +```console clever k8s version update [options] ``` **Arguments** -``` + +```console cluster-id|cluster-name Kubernetes cluster ID or name ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) --target Target version to upgrade to (e.g.: 24, 2.4, 2.4.1) ``` @@ -1930,12 +2133,14 @@ cluster-id|cluster-name Kubernetes cluster ID or name **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -1946,12 +2151,14 @@ clever keycloak [options] **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak disable-ng ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -1962,12 +2169,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak enable-ng ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -1978,17 +2187,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak get [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -1999,12 +2211,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak open ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2015,12 +2229,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak open logs ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2031,12 +2247,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak open webui ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2047,12 +2265,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak rebuild ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2063,12 +2283,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak restart ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2079,17 +2301,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak version [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2100,17 +2325,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak version check [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2121,17 +2349,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever keycloak version update [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console --target Target version to upgrade to (e.g.: 24, 2.4, 2.4.1) ``` @@ -2142,18 +2373,21 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.11.0 **Usage** -``` + +```console clever kv [options] ``` **Arguments** -``` + +```console kv-id|addon-id|addon-name Add-on/Real ID (or name, if unambiguous) of a Materia KV or Redis® add-on command The raw command to send to the Materia KV or Redis® add-on ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2165,17 +2399,20 @@ command The raw command to send to the Materia K **Since:** 0.2.0 **Usage** -``` + +```console clever link [options] ``` **Arguments** -``` + +```console app-id|app-name Application ID (or name, if unambiguous) ``` **Options** -``` + +```console -a, --alias Short name for the application -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2187,12 +2424,14 @@ app-id|app-name Application ID (or name, if unambiguous) **Since:** 0.2.0 **Usage** -``` + +```console clever login [options] ``` **Options** -``` + +```console -a, --alias Profile alias (default: default) --api-host API host URL override --auth-bridge-host Auth bridge URL override @@ -2211,12 +2450,14 @@ clever login [options] **Since:** 1.0.0 **Usage** -``` + +```console clever logout [options] ``` **Options** -``` + +```console -a, --alias Alias of the profile to log out ``` @@ -2227,12 +2468,14 @@ clever logout [options] **Since:** 0.2.0 **Usage** -``` + +```console clever logs [options] ``` **Options** -``` + +```console --addon Add-on ID or real ID --after, --since Fetch logs after this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h) -a, --alias Short name for the application @@ -2250,12 +2493,14 @@ clever logs [options] **Since:** 0.5.0 **Usage** -``` + +```console clever make-default ``` **Arguments** -``` + +```console app-alias Application alias ``` @@ -2266,12 +2511,14 @@ app-alias Application alias **Since:** 3.13.0 **Usage** -``` + +```console clever matomo [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2282,17 +2529,20 @@ clever matomo [options] **Since:** 3.13.0 **Usage** -``` + +```console clever matomo get [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2303,12 +2553,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever matomo open ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2319,12 +2571,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever matomo open logs ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2335,12 +2589,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever matomo open webui ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2351,12 +2607,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever matomo rebuild ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2367,12 +2625,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever matomo restart ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2383,12 +2643,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2399,17 +2661,20 @@ clever metabase [options] **Since:** 3.13.0 **Usage** -``` + +```console clever metabase get [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2420,12 +2685,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase open ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2436,12 +2703,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase open logs ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2452,12 +2721,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase open webui ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2468,12 +2739,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase rebuild ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2484,12 +2757,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase restart ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -2500,17 +2775,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase version [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2521,17 +2799,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase version check [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2542,17 +2823,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever metabase version update [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console --target Target version to upgrade to (e.g.: 24, 2.4, 2.4.1) ``` @@ -2563,12 +2847,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.12.0 **Usage** -``` + +```console clever ng [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2580,17 +2866,20 @@ clever ng [options] **Since:** 3.12.0 **Usage** -``` + +```console clever ng create [options] ``` **Arguments** -``` + +```console ng-label Network Group label ``` **Options** -``` + +```console --description Network Group description --link Comma separated list of members IDs to link to a Network Group (app_xxx, external_xxx, mysql_xxx, postgresql_xxx, redis_xxx, etc.) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) @@ -2604,19 +2893,22 @@ ng-label Network Group label **Since:** 3.12.0 **Usage** -``` + +```console clever ng create external [options] ``` **Arguments** -``` + +```console external-peer-label External peer label ng-id|ng-label Network Group ID or label public-key WireGuard public key of the external peer to link to a Network Group ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2627,17 +2919,20 @@ public-key WireGuard public key of the external pee **Since:** 3.12.0 **Usage** -``` + +```console clever ng delete [options] ``` **Arguments** -``` + +```console ng-id|ng-label Network Group ID or label ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2648,18 +2943,21 @@ ng-id|ng-label Network Group ID or label **Since:** 3.12.0 **Usage** -``` + +```console clever ng delete external [options] ``` **Arguments** -``` + +```console peer-id|peer-label External peer ID or label ng-id|ng-label Network Group ID or label ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2670,17 +2968,20 @@ ng-id|ng-label Network Group ID or label **Since:** 3.12.0 **Usage** -``` + +```console clever ng get [options] ``` **Arguments** -``` + +```console id|label ID or Label of a Network Group, a member or an (external) peer ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) --type Type of resource to look for (NetworkGroup, Member, CleverPeer, ExternalPeer) @@ -2693,18 +2994,21 @@ id|label ID or Label of a Network Group, a member **Since:** 3.12.0 **Usage** -``` + +```console clever ng get-config [options] ``` **Arguments** -``` + +```console peer-id|peer-label External peer ID or label ng-id|ng-label Network Group ID or label ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2715,18 +3019,21 @@ ng-id|ng-label Network Group ID or label **Since:** 3.12.0 **Usage** -``` + +```console clever ng link [options] ``` **Arguments** -``` + +```console id ID of a resource to (un)link to a Network Group ng-id|ng-label Network Group ID or label ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2737,17 +3044,20 @@ ng-id|ng-label Network Group ID or label **Since:** 3.12.0 **Usage** -``` + +```console clever ng search [options] ``` **Arguments** -``` + +```console id|label ID or Label of a Network Group, a member or an (external) peer ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) --type Type of resource to look for (NetworkGroup, Member, CleverPeer, ExternalPeer) @@ -2760,18 +3070,21 @@ id|label ID or Label of a Network Group, a member **Since:** 3.12.0 **Usage** -``` + +```console clever ng unlink [options] ``` **Arguments** -``` + +```console id ID of a resource to (un)link to a Network Group ng-id|ng-label Network Group ID or label ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2782,12 +3095,14 @@ ng-id|ng-label Network Group ID or label **Since:** 0.6.1 **Usage** -``` + +```console clever notify-email [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) --list-all List all notifications for your user or for an organisation with the '--org' option -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) @@ -2800,17 +3115,20 @@ clever notify-email [options] **Since:** 0.6.1 **Usage** -``` + +```console clever notify-email add --notify [options] ``` **Arguments** -``` + +```console name Notification name ``` **Options** -``` + +```console --notify Notify a user, a specific email address or the whole organisation (multiple values allowed, comma separated) (required) --event Restrict notifications to specific event types -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) @@ -2824,17 +3142,20 @@ name Notification name **Since:** 0.6.1 **Usage** -``` + +```console clever notify-email remove [options] ``` **Arguments** -``` + +```console notification-id Notification ID ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` @@ -2845,7 +3166,8 @@ notification-id Notification ID **Since:** 4.8.0 **Usage** -``` + +```console clever oauth-consumers ``` @@ -2856,17 +3178,20 @@ clever oauth-consumers **Since:** 4.8.0 **Usage** -``` + +```console clever oauth-consumers create [options] ``` **Arguments** -``` + +```console name Consumer name ``` **Options** -``` + +```console --base-url OAuth callback base URL -d, --description Consumer description -F, --format Output format (human, json) (default: human) @@ -2883,17 +3208,20 @@ name Consumer name **Since:** 4.8.0 **Usage** -``` + +```console clever oauth-consumers delete [options] ``` **Arguments** -``` + +```console consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) ``` **Options** -``` + +```console -y, --yes Skip confirmation prompts ``` @@ -2904,17 +3232,20 @@ consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) **Since:** 4.8.0 **Usage** -``` + +```console clever oauth-consumers get [options] ``` **Arguments** -``` + +```console consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) --with-secret Include the consumer secret in the output ``` @@ -2926,12 +3257,14 @@ consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) **Since:** 4.8.0 **Usage** -``` + +```console clever oauth-consumers list [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -2942,12 +3275,14 @@ clever oauth-consumers list [options] **Since:** 4.8.0 **Usage** -``` + +```console clever oauth-consumers open ``` **Arguments** -``` + +```console consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) ``` @@ -2958,17 +3293,20 @@ consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) **Since:** 4.8.0 **Usage** -``` + +```console clever oauth-consumers update [options] ``` **Arguments** -``` + +```console consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) ``` **Options** -``` + +```console --base-url OAuth callback base URL -d, --description Consumer description -F, --format Output format (human, json) (default: human) @@ -2985,12 +3323,14 @@ consumer-key|consumer-name OAuth consumer key (or name, if unambiguous) **Since:** 0.5.0 **Usage** -``` + +```console clever open [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -3002,12 +3342,14 @@ clever open [options] **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3018,12 +3360,14 @@ clever otoroshi [options] **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi disable-ng ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3034,12 +3378,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi enable-ng ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3050,17 +3396,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi get [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3071,12 +3420,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 4.4.0 **Usage** -``` + +```console clever otoroshi get-config ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3087,12 +3438,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi open ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3103,12 +3456,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi open logs ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3119,12 +3474,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 4.9.0 **Usage** -``` + +```console clever otoroshi open swaggerui ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3135,12 +3492,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi open webui ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3151,12 +3510,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi rebuild ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3167,12 +3528,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi restart ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` @@ -3183,17 +3546,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi version [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3204,17 +3570,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi version check [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3225,17 +3594,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 3.13.0 **Usage** -``` + +```console clever otoroshi version update [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console --target Target version to upgrade to (e.g.: 24, 2.4, 2.4.1) ``` @@ -3246,12 +3618,14 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 0.10.1 **Usage** -``` + +```console clever profile [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3262,12 +3636,14 @@ clever profile [options] **Since:** 4.6.0 **Usage** -``` + +```console clever profile list [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3278,7 +3654,8 @@ clever profile list [options] **Since:** 3.11.0 **Usage** -``` + +```console clever profile open ``` @@ -3289,12 +3666,14 @@ clever profile open **Since:** 4.6.0 **Usage** -``` + +```console clever profile switch [options] ``` **Options** -``` + +```console -a, --alias Alias of the profile to switch to ``` @@ -3305,12 +3684,14 @@ clever profile switch [options] **Since:** 0.5.0 **Usage** -``` + +```console clever published-config [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -F, --format Output format (human, json, shell) (default: human) @@ -3324,12 +3705,14 @@ clever published-config [options] **Since:** 0.5.0 **Usage** -``` + +```console clever published-config import [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) --json Import variables as JSON (an array of { "name": "THE_NAME", "value": "THE_VALUE" } objects) @@ -3342,40 +3725,46 @@ clever published-config import [options] **Since:** 0.5.0 **Usage** -``` + +```console clever published-config rm [options] ``` **Arguments** -``` + +```console variable-name Name of the environment variable ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` ### published-config set -**Description:** Add or update a published configuration item named with the value +**Description:** Add or update a published configuration item named `` with the value `` **Since:** 0.5.0 **Usage** -``` + +```console clever published-config set [options] ``` **Arguments** -``` + +```console variable-name Name of the environment variable variable-value Value of the environment variable ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -3387,12 +3776,14 @@ variable-value Value of the environment variable **Since:** 0.4.0 **Usage** -``` + +```console clever restart [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) --commit Restart the application with a specific commit ID @@ -3409,12 +3800,14 @@ clever restart [options] **Since:** 0.4.0 **Usage** -``` + +```console clever scale [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) --build-flavor The size of the build instance, or 'disabled' if you want to disable dedicated build instances @@ -3433,12 +3826,14 @@ clever scale [options] **Since:** 0.5.0 **Usage** -``` + +```console clever service [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -F, --format Output format (human, json) (default: human) @@ -3454,17 +3849,20 @@ clever service [options] **Since:** 0.5.0 **Usage** -``` + +```console clever service link-addon [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -3476,17 +3874,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 0.5.0 **Usage** -``` + +```console clever service link-app [options] ``` **Arguments** -``` + +```console app-id|app-name Application ID (or name, if unambiguous) ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -3498,17 +3899,20 @@ app-id|app-name Application ID (or name, if unambiguous) **Since:** 0.5.0 **Usage** -``` + +```console clever service unlink-addon [options] ``` **Arguments** -``` + +```console addon-id|addon-name Add-on ID (or name, if unambiguous) ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -3520,17 +3924,20 @@ addon-id|addon-name Add-on ID (or name, if unambiguous) **Since:** 0.5.0 **Usage** -``` + +```console clever service unlink-app [options] ``` **Arguments** -``` + +```console app-id|app-name Application ID (or name, if unambiguous) ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -3542,12 +3949,14 @@ app-id|app-name Application ID (or name, if unambiguous) **Since:** 0.7.0 **Usage** -``` + +```console clever ssh [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -c, --command Execute a command on the remote instance and exit @@ -3561,12 +3970,14 @@ clever ssh [options] **Since:** 3.13.0 **Usage** -``` + +```console clever ssh-keys [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3577,12 +3988,14 @@ clever ssh-keys [options] **Since:** 3.13.0 **Usage** -``` + +```console clever ssh-keys add ``` **Arguments** -``` + +```console ssh-key-name SSH key name ssh-key-path SSH public key path (.pub) ``` @@ -3594,7 +4007,8 @@ ssh-key-path SSH public key path (.pub) **Since:** 3.13.0 **Usage** -``` + +```console clever ssh-keys open ``` @@ -3605,12 +4019,14 @@ clever ssh-keys open **Since:** 3.13.0 **Usage** -``` + +```console clever ssh-keys remove ``` **Arguments** -``` + +```console ssh-key-name SSH key name ``` @@ -3621,12 +4037,14 @@ ssh-key-name SSH key name **Since:** 3.13.0 **Usage** -``` + +```console clever ssh-keys remove-all [options] ``` **Options** -``` + +```console -y, --yes Skip confirmation and remove all SSH keys directly ``` @@ -3637,12 +4055,14 @@ clever ssh-keys remove-all [options] **Since:** 0.2.0 **Usage** -``` + +```console clever status [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -F, --format Output format (human, json) (default: human) @@ -3655,12 +4075,14 @@ clever status [options] **Since:** 0.2.0 **Usage** -``` + +```console clever stop [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) ``` @@ -3672,12 +4094,14 @@ clever stop [options] **Since:** 2.3.0 **Usage** -``` + +```console clever tcp-redirs [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -F, --format Output format (human, json) (default: human) @@ -3690,12 +4114,14 @@ clever tcp-redirs [options] **Since:** 2.3.0 **Usage** -``` + +```console clever tcp-redirs add --namespace [options] ``` **Options** -``` + +```console --namespace Namespace in which the TCP redirection should be (required) -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -3708,12 +4134,14 @@ clever tcp-redirs add --namespace [options] **Since:** 2.3.0 **Usage** -``` + +```console clever tcp-redirs list-namespaces [options] ``` **Options** -``` + +```console -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) -F, --format Output format (human, json) (default: human) @@ -3726,17 +4154,20 @@ clever tcp-redirs list-namespaces [options] **Since:** 2.3.0 **Usage** -``` + +```console clever tcp-redirs remove --namespace [options] ``` **Arguments** -``` + +```console port port identifying the TCP redirection ``` **Options** -``` + +```console --namespace Namespace in which the TCP redirection should be (required) -a, --alias Short name for the application --app Application to manage by its ID (or name, if unambiguous) @@ -3744,17 +4175,19 @@ port port identifying the TCP redirection ## tokens -**Description:** Manage API tokens to query Clever Cloud API from https://api-bridge.clever-cloud.com +**Description:** Manage API tokens to query Clever Cloud API from **Since:** 3.12.0 **Usage** -``` + +```console clever tokens [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) ``` @@ -3765,17 +4198,20 @@ clever tokens [options] **Since:** 3.12.0 **Usage** -``` + +```console clever tokens create [options] ``` **Arguments** -``` + +```console api-token-name API token name ``` **Options** -``` + +```console -e, --expiration Duration until API token expiration (e.g.: 1h, 4d, 2w, 6M) (default: 1y) -F, --format Output format (human, json) (default: human) ``` @@ -3787,12 +4223,14 @@ api-token-name API token name **Since:** 3.12.0 **Usage** -``` + +```console clever tokens revoke ``` **Arguments** -``` + +```console api-token-id API token ID ``` @@ -3803,12 +4241,14 @@ api-token-id API token ID **Since:** 0.2.0 **Usage** -``` + +```console clever unlink ``` **Arguments** -``` + +```console app-alias Application alias ``` @@ -3819,7 +4259,8 @@ app-alias Application alias **Since:** 1.0.0 **Usage** -``` + +```console clever version ``` @@ -3830,12 +4271,14 @@ clever version **Since:** 0.6.0 **Usage** -``` + +```console clever webhooks [options] ``` **Options** -``` + +```console -F, --format Output format (human, json) (default: human) --list-all List all notifications for your user or for an organisation with the '--org' option -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) @@ -3848,18 +4291,21 @@ clever webhooks [options] **Since:** 0.6.0 **Usage** -``` + +```console clever webhooks add [options] ``` **Arguments** -``` + +```console name Notification name url Webhook URL ``` **Options** -``` + +```console --event Restrict notifications to specific event types --format Format of the body sent to the webhook ('raw', 'slack', 'gitter', or 'flowdock') (default: raw) -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) @@ -3873,21 +4319,24 @@ url Webhook URL **Since:** 0.6.0 **Usage** -``` + +```console clever webhooks remove [options] ``` **Arguments** -``` + +```console notification-id Notification ID ``` **Options** -``` + +```console -o, --org, --owner Organisation to target by its ID (or name, if unambiguous) ``` ## Clever Cloud complete documentation -For more comprehensive information about Clever Cloud, read the complete documentation: https://www.clever-cloud.com/developers/doc/ -Clever Cloud complete documentation is available in a LLM-optimized format: https://www.clever-cloud.com/developers/llms.txt +For more comprehensive information about Clever Cloud, read the complete documentation: +Clever Cloud complete documentation is available in a LLM-optimized format: diff --git a/content/doc/reference/products-support.md b/content/doc/reference/products-support.md index d67c590a7..7e8c469c0 100644 --- a/content/doc/reference/products-support.md +++ b/content/doc/reference/products-support.md @@ -16,7 +16,7 @@ This is a product matrix showing the services that are available with our Terraf ## Add-ons available on Terraform and K8S-Operator | Product | Terraform | K8S operator | -|-----------------|-----------|--------------| +| --------------- | --------- | ------------ | | Cellar | ✅ | | | Config Provider | ✅ | ✅ | | Elastic Stack | ✅ | ✅ | @@ -36,7 +36,7 @@ This is a product matrix showing the services that are available with our Terraf ## Runtimes available on Terraform and K8S-Operator | Product | Terraform | K8S operator | -|-------------|-----------|--------------| +| ----------- | --------- | ------------ | | .NET | ✅ | | | Docker | ✅ | | | FrankenPHP | ✅ | | diff --git a/content/doc/reference/reference-environment-variables.md b/content/doc/reference/reference-environment-variables.md index e913296de..8212c5bae 100644 --- a/content/doc/reference/reference-environment-variables.md +++ b/content/doc/reference/reference-environment-variables.md @@ -24,64 +24,64 @@ aliases: These are read-only variables that are generated for each scaler before they build and start your application. -| Name | Description | Example value | -|-----------------------|------------------------------|--------------------------------| -|[`INSTANCE_NUMBER`](/doc/develop/env-variables/#what-is-the-instance_number-variable-used-for "What is the INSTANCE_NUMBER variable used for?") | Allows your application to differentiate each running node on the application level. | 0, 1… | -|`INSTANCE_TYPE` | Whether this instance is a "build" instance or a "production" instance. | build, production | -|`INSTANCE_ID` | The ID of the current instance (scaler) of your application. It's unique for each instance of your application and changes every time you deploy it. | 518c8d8f-e304-4310-81e0-9c4237d55645 | -|`CC_PRETTY_INSTANCE_NAME` | The random generated string as instance pretty name using pokemon names. | Tiny rhyhorn | -|`APP_ID` | The ID of your Clever Cloud application | app_649a93d1-6677-44bc-aca7-6f46107d6e02 | -|`APP_HOME` | The absolute path to your application folder | /home/bas/app_649a93d1-6677-44bc-aca7-6f46107d6e02 | -|`CC_DEPLOYMENT_ID` | Internal id of current deployment | f7efaf04-1a63-45a1-8503-0de7c750ee48 | -|`CC_COMMIT_ID` | The id of the commit that's currently running | d88cd2ae1aaa91923ed2bd689d95d713b6f3f45f | -|`CC_REVERSE_PROXY_IPS` | A comma separated list of trusted IP addresses. You should only accept requests coming from these IP addresses. | x.y.z.z,x.y.z.z | -|`ELASTIC_APM_SERVICE_NAME` | Sets the name of your service/application in Elastic APM. Automatically defined when you have linked an Elastic APM service to your application. You can override it by defining it yourself | Your application's name conforming to Elastic APM naming convention | -|`CC_APP_NAME` | The customer defined application name | cloud-api-production | -|`PORT` | The mandatory port value is 8080 | 8080 | +| Name | Description | Example value | +| ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [`INSTANCE_NUMBER`](/doc/develop/env-variables/#what-is-the-instance_number-variable-used-for "What is the INSTANCE_NUMBER variable used for?") | Allows your application to differentiate each running node on the application level. | 0, 1… | +| `INSTANCE_TYPE` | Whether this instance is a "build" instance or a "production" instance. | build, production | +| `INSTANCE_ID` | The ID of the current instance (scaler) of your application. It's unique for each instance of your application and changes every time you deploy it. | 518c8d8f-e304-4310-81e0-9c4237d55645 | +| `CC_PRETTY_INSTANCE_NAME` | The random generated string as instance pretty name using pokemon names. | Tiny rhyhorn | +| `APP_ID` | The ID of your Clever Cloud application | app_649a93d1-6677-44bc-aca7-6f46107d6e02 | +| `APP_HOME` | The absolute path to your application folder | /home/bas/app_649a93d1-6677-44bc-aca7-6f46107d6e02 | +| `CC_DEPLOYMENT_ID` | Internal id of current deployment | f7efaf04-1a63-45a1-8503-0de7c750ee48 | +| `CC_COMMIT_ID` | The id of the commit that's currently running | d88cd2ae1aaa91923ed2bd689d95d713b6f3f45f | +| `CC_REVERSE_PROXY_IPS` | A comma separated list of trusted IP addresses. You should only accept requests coming from these IP addresses. | x.y.z.z,x.y.z.z | +| `ELASTIC_APM_SERVICE_NAME` | Sets the name of your service/application in Elastic APM. Automatically defined when you have linked an Elastic APM service to your application. You can override it by defining it yourself | Your application's name conforming to Elastic APM naming convention | +| `CC_APP_NAME` | The customer defined application name | cloud-api-production | +| `PORT` | The mandatory port value is 8080 | 8080 | ### Variables you can define So you can alter the build&start process for your application. -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`APP_FOLDER` | Folder in which the application is located (inside the git repository) | | -|`CC_DISABLE_JVM_AGENT` | Disable JVM agent activation | | -|`CC_GIT_FULL_CLONE` | If set to `false`, the git repository is cloned with `--depth 1` to speed up the deployment | true | -|`CC_DISABLE_GIT_SUBMODULES` | Disable Git submodules initialization & synchronization | | -|`CC_PREVENT_LOGSCOLLECTION` | Disable Vector service used to collect and send logs | `false`| -|[`CC_HEALTH_CHECK_PATH`](/doc/develop/healthcheck) | Custom path to validate your application deployment | | -|`CC_SSH_PRIVATE_KEY` | A ssh private key to setup for the user running your application | | -|`CC_SSH_PRIVATE_KEY_FILE` | The name to use for the file containing the private ssh key | id_ed25519 | -|`CC_WORKER_RESTART` | One of `always`, `on-failure` or `no`. Control whether workers need to be restarted when they exit. This setting controls all workers. | on-failure | -|`CC_WORKER_RESTART_DELAY` | Define a delay in seconds to restart the worker when they exit. | 1 | -|[`CC_WORKER_COMMAND`](/doc/develop/workers) | Command to run in background as a worker process. You can run multiple workers. | | +| Name | Description | Default value | +| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `APP_FOLDER` | Folder in which the application is located (inside the git repository) | | +| `CC_DISABLE_JVM_AGENT` | Disable JVM agent activation | | +| `CC_GIT_FULL_CLONE` | If set to `false`, the git repository is cloned with `--depth 1` to speed up the deployment | true | +| `CC_DISABLE_GIT_SUBMODULES` | Disable Git submodules initialization & synchronization | | +| `CC_PREVENT_LOGSCOLLECTION` | Disable Vector service used to collect and send logs | `false` | +| [`CC_HEALTH_CHECK_PATH`](/doc/develop/healthcheck) | Custom path to validate your application deployment | | +| `CC_SSH_PRIVATE_KEY` | A ssh private key to setup for the user running your application | | +| `CC_SSH_PRIVATE_KEY_FILE` | The name to use for the file containing the private ssh key | id_ed25519 | +| `CC_WORKER_RESTART` | One of `always`, `on-failure` or `no`. Control whether workers need to be restarted when they exit. This setting controls all workers. | on-failure | +| `CC_WORKER_RESTART_DELAY` | Define a delay in seconds to restart the worker when they exit. | 1 | +| [`CC_WORKER_COMMAND`](/doc/develop/workers) | Command to run in background as a worker process. You can run multiple workers. | | You can set some tools' version in any runtime (except Docker): -| Name | Description | -|------|-------------| -| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.161` | | -| `CC_NODE_VERSION` | Set Node.js version, for example `24`, `23.11` or `22.15.1` | | +| Name | Description | +| ----------------- | ----------------------------------------------------------- | +| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.161` | +| `CC_NODE_VERSION` | Set Node.js version, for example `24`, `23.11` or `22.15.1` | #### Control build and dependencies cache -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`CC_CACHE_DEPENDENCIES` | Enable caching of your build dependencies to speed up following builds. | false | -|`CC_CACHE_DEPENDENCIES_EXTRA_PATHS` | Add custom paths to the dependencies cache. Expected format: Colon-separated PATH-style string. Paths are considered relative to APP_HOME. One level of `../` is allowed. E.g. `../.cargo:lib:vendor` will select `/home/bas/.cargo`, `/home/bas/{app_id}/lib` and `/home/bas/{app_id}/vendor` | | -|[`CC_DISABLE_BUILD_CACHE_UPLOAD`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Disable creation and upload of cache archive. Restarts won't be speeded up. | `false` | -|[`CC_IGNORE_FROM_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Allows to specify paths to ignore when the build cache archive is created. | | -|[`IGNORE_FROM_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | (Deprecated) Allows to specify paths to ignore when the build cache archive is created. | | -|[`CC_OVERRIDE_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Allows to specify paths that will be in the build cache. Only those files / directories will be cached | | +| Name | Description | Default value | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `CC_CACHE_DEPENDENCIES` | Enable caching of your build dependencies to speed up following builds. | false | +| `CC_CACHE_DEPENDENCIES_EXTRA_PATHS` | Add custom paths to the dependencies cache. Expected format: Colon-separated PATH-style string. Paths are considered relative to APP_HOME. One level of `../` is allowed. E.g. `../.cargo:lib:vendor` will select `/home/bas/.cargo`, `/home/bas/{app_id}/lib` and `/home/bas/{app_id}/vendor` | | +| [`CC_DISABLE_BUILD_CACHE_UPLOAD`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Disable creation and upload of cache archive. Restarts won't be speeded up. | `false` | +| [`CC_IGNORE_FROM_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Allows to specify paths to ignore when the build cache archive is created. | | +| [`IGNORE_FROM_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | (Deprecated) Allows to specify paths to ignore when the build cache archive is created. | | +| [`CC_OVERRIDE_BUILDCACHE`](/doc/develop/env-variables/#settings-you-can-define-using-environment-variables "Settings you can define using environment variables") | Allows to specify paths that will be in the build cache. Only those files / directories will be cached | | #### Control the deployment's behavior -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`CC_RUN_COMMAND` | Custom command to run your application. | | -|`CC_TASK` | If set to true, the deployer runs `CC_RUN_COMMAND` and terminates the instance after running the task. Trigger an execution using `git push` or starting your instance | false | -|[`CC_TROUBLESHOOT`](/doc/find-help/troubleshooting "Troubleshooting") | Enable debug log level, will also keep the VM up after failure for 15 minutes so you can SSH and debug. Don't forget to cancel deployment if you push a new commit. | false | +| Name | Description | Default value | +| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `CC_RUN_COMMAND` | Custom command to run your application. | | +| `CC_TASK` | If set to true, the deployer runs `CC_RUN_COMMAND` and terminates the instance after running the task. Trigger an execution using `git push` or starting your instance | false | +| [`CC_TROUBLESHOOT`](/doc/find-help/troubleshooting "Troubleshooting") | Enable debug log level, will also keep the VM up after failure for 15 minutes so you can SSH and debug. Don't forget to cancel deployment if you push a new commit. | false | {{< callout emoji="🐳" >}} `CC_RUN_COMMAND` has no effect on Docker. To run Docker, use `CMD` in your [Dockerfile](/doc/applications/docker/#dockerized-rust-application-deployment). @@ -91,38 +91,38 @@ You can set some tools' version in any runtime (except Docker): Use these to define [commands to run](/doc/develop/build-hooks) between various steps of the deployment. -| Name | Description | -|-----------------------|------------------------------| -|[`CC_PRE_BUILD_HOOK`](/doc/develop/build-hooks/#pre-build "Pre build hook") | Ran before the dependencies are fetched. If it fails, the deployment fails. | -|[`CC_POST_BUILD_HOOK`](/doc/develop/build-hooks/#post-build "Post build hook") | Ran after the project is built, and before the cache archive is generated. If it fails, the deployment fails. | -|[`CC_PRE_RUN_HOOK`](/doc/develop/build-hooks/#pre-run "Pre run hook") | Ran before the application is started, but after the cache archive has been generated. If it fails, the deployment fails. | -|[`CC_RUN_FAILED_HOOK`](/doc/develop/build-hooks/#run-successfail "Run succeeded/failed hook") | Ran once the application has failed to start. | -|[`CC_RUN_SUCCEEDED_HOOK`](/doc/develop/build-hooks/#run-successfail "Run succeeded/failed hook" )| Ran once the application has started successfuly. | +| Name | Description | +| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| [`CC_PRE_BUILD_HOOK`](/doc/develop/build-hooks/#pre-build "Pre build hook") | Ran before the dependencies are fetched. If it fails, the deployment fails. | +| [`CC_POST_BUILD_HOOK`](/doc/develop/build-hooks/#post-build "Post build hook") | Ran after the project is built, and before the cache archive is generated. If it fails, the deployment fails. | +| [`CC_PRE_RUN_HOOK`](/doc/develop/build-hooks/#pre-run "Pre run hook") | Ran before the application is started, but after the cache archive has been generated. If it fails, the deployment fails. | +| [`CC_RUN_FAILED_HOOK`](/doc/develop/build-hooks/#run-successfail "Run succeeded/failed hook") | Ran once the application has failed to start. | +| [`CC_RUN_SUCCEEDED_HOOK`](/doc/develop/build-hooks/#run-successfail "Run succeeded/failed hook" ) | Ran once the application has started successfuly. | #### Configure extra software -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|[`CC_CLAMAV`](/doc/administrate/clamav "Clamav") | Start the clamav and clamav-freshclam services (the database is updated every 2 hours). WARNING: Clamscan consumes a lot of resources (~ 1GB of memory), make sure you have a scaler with enough memory to avoid OOM. | false | -|[`CC_CLAMAV_MAXQUEUE`](/doc/administrate/clamav "Clamav") | Maximum number of queued items. | 100 | -|[`CC_CLAMAV_MAXTHREADS`](/doc/administrate/clamav "Clamav") | Maximum number of threads running at the same time. | 10 | -|[`CC_METRICS_PROMETHEUS_PASSWORD`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the password for the basic auth of the Prometheus endpoint | | -|[`CC_METRICS_PROMETHEUS_PATH`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the path on which the Prometheus endpoint is available | /metrics | -|[`CC_METRICS_PROMETHEUS_PORT`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the port on which the Prometheus endpoint is available | 9100 | -|[`CC_METRICS_PROMETHEUS_RESPONSE_TIMEOUT`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the timeout in seconds to collect the application metrics. This value **must** be below 60 seconds as data are collected every minutes | 3 | -|[`CC_METRICS_PROMETHEUS_USER`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the user for the basic auth of the Prometheus endpoint | | -|[`CC_REQUEST_FLOW`](/doc/develop/request-flow "Request Flow") | Comma-separated list of middleware to chain between the public port and your application (`block`, `custom`, `oauth2-proxy`, `otoroshi-challenge`, `redirectionio`, `varnish`). Special values: `disable`, `block` | | -|[`CC_REQUEST_FLOW_CUSTOM`](/doc/develop/request-flow/#custom-middleware "Request Flow") | Command starting a custom middleware, with the `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders | | -|[`CC_VARNISH_FILE`](/doc/develop/varnish "Cache") | The path to the Varnish configuration file, relative to your application root | `/clevercloud/varnish.vcl` | -|[`CC_VARNISH_STORAGE_SIZE`](/doc/develop/varnish "Cache") | Configure the size of the Varnish cache. | 1G | -|[`CC_WORKER_COMMAND`](/doc/develop/workers "Workers") | Command to run in background as a worker process. You can run multiple workers. | | +| Name | Description | Default value | +| ------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | +| [`CC_CLAMAV`](/doc/administrate/clamav "Clamav") | Start the clamav and clamav-freshclam services (the database is updated every 2 hours). WARNING: Clamscan consumes a lot of resources (~ 1GB of memory), make sure you have a scaler with enough memory to avoid OOM. | false | +| [`CC_CLAMAV_MAXQUEUE`](/doc/administrate/clamav "Clamav") | Maximum number of queued items. | 100 | +| [`CC_CLAMAV_MAXTHREADS`](/doc/administrate/clamav "Clamav") | Maximum number of threads running at the same time. | 10 | +| [`CC_METRICS_PROMETHEUS_PASSWORD`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the password for the basic auth of the Prometheus endpoint | | +| [`CC_METRICS_PROMETHEUS_PATH`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the path on which the Prometheus endpoint is available | /metrics | +| [`CC_METRICS_PROMETHEUS_PORT`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the port on which the Prometheus endpoint is available | 9100 | +| [`CC_METRICS_PROMETHEUS_RESPONSE_TIMEOUT`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the timeout in seconds to collect the application metrics. This value **must** be below 60 seconds as data are collected every minutes | 3 | +| [`CC_METRICS_PROMETHEUS_USER`](/doc/metrics/#publish-your-own-metrics "Publish your own metrics") | Define the user for the basic auth of the Prometheus endpoint | | +| [`CC_REQUEST_FLOW`](/doc/develop/request-flow "Request Flow") | Comma-separated list of middleware to chain between the public port and your application (`block`, `custom`, `oauth2-proxy`, `otoroshi-challenge`, `redirectionio`, `varnish`). Special values: `disable`, `block` | | +| [`CC_REQUEST_FLOW_CUSTOM`](/doc/develop/request-flow/#custom-middleware "Request Flow") | Command starting a custom middleware, with the `@@LISTEN_PORT@@` and `@@FORWARD_PORT@@` placeholders | | +| [`CC_VARNISH_FILE`](/doc/develop/varnish "Cache") | The path to the Varnish configuration file, relative to your application root | `/clevercloud/varnish.vcl` | +| [`CC_VARNISH_STORAGE_SIZE`](/doc/develop/varnish "Cache") | Configure the size of the Varnish cache. | 1G | +| [`CC_WORKER_COMMAND`](/doc/develop/workers "Workers") | Command to run in background as a worker process. You can run multiple workers. | | {{% content "mise" %}} -| Name | Description | Default value | -|-----------------|--------------------------|--------------------------------| -|`CC_DISABLE_MISE`| Set to `true` to disable Mise | | -|`CC_MISE_FILE_PATH`| Path to the `mise.toml` file, relative to your application root | `mise.toml` | +| Name | Description | Default value | +| ------------------- | --------------------------------------------------------------- | ------------- | +| `CC_DISABLE_MISE` | Set to `true` to disable Mise | | +| `CC_MISE_FILE_PATH` | Path to the `mise.toml` file, relative to your application root | `mise.toml` | {{% content "redirectionio" %}} > [!NOTE] Redirection.io is not available in Docker and PHP applications @@ -133,12 +133,12 @@ Use these to define [commands to run](/doc/develop/build-hooks) between various Note that `Reusable keys` are required to use multiple instances. You can [generate one here](https://login.tailscale.com/admin/settings/keys "Generate tailscale key"). -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -| [`TAILSCALE_ACCEPT_DNS`](https://tailscale.com/kb/1072/client-preferences#use-tailscale-dns-settings) | Tailscale use its default DNS settings | `true` | -| [`TAILSCALE_ACCEPT_ROUTES`](https://tailscale.com/kb/1072/client-preferences#use-tailscale-subnets) | Tailscale uses its subnets settings | `false` | -| [`TAILSCALE_AUTH_KEY`](https://tailscale.com/ "tailscale.com") | Contains your Tailscale Auth key | | -| `TAILSCALE_LOGIN_SERVER`| Contains the login server | | +| Name | Description | Default value | +| ----------------------------------------------------------------------------------------------------- | -------------------------------------- | ------------- | +| [`TAILSCALE_ACCEPT_DNS`](https://tailscale.com/kb/1072/client-preferences#use-tailscale-dns-settings) | Tailscale use its default DNS settings | `true` | +| [`TAILSCALE_ACCEPT_ROUTES`](https://tailscale.com/kb/1072/client-preferences#use-tailscale-subnets) | Tailscale uses its subnets settings | `false` | +| [`TAILSCALE_AUTH_KEY`](https://tailscale.com/ "tailscale.com") | Contains your Tailscale Auth key | | +| `TAILSCALE_LOGIN_SERVER` | Contains the login server | | #### How it works? @@ -150,108 +150,108 @@ If `TAILSCALE_LOGIN_SERVER` is provided, the agent will be configured to reach a [Docker Documentation](/doc/applications/docker) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`CC_DOCKERFILE` | The name of the Dockerfile to build. | Dockerfile | -|`CC_DOCKER_BUILDX` | Set to `false` to use Docker legacy build | true | -|`CC_DOCKER_EXPOSED_HTTP_PORT` | Set to custom HTTP port if your Docker container runs on custom port. | 8080 | -|`CC_DOCKER_EXPOSED_TCP_PORT` | Set to custom TCP port if your Docker container runs on custom port. | 4040 | -|`CC_DOCKER_FIXED_CIDR_V6` | Activate the support of IPv6 with an IPv6 subnet int the docker daemon. | | -|`CC_DOCKER_LOGIN_PASSWORD` | The password of your username. | | -|`CC_DOCKER_LOGIN_SERVER` | The server of your private registry (optional). | Docker’s public registry | -|`CC_DOCKER_LOGIN_USERNAME` | The username to login to a private registry. | | -|`CC_MOUNT_DOCKER_SOCKET` | Set to true to access the host Docker socket from inside your container. | false | +| Name | Description | Default value | +| ----------------------------- | ------------------------------------------------------------------------ | ------------------------ | +| `CC_DOCKERFILE` | The name of the Dockerfile to build. | Dockerfile | +| `CC_DOCKER_BUILDX` | Set to `false` to use Docker legacy build | true | +| `CC_DOCKER_EXPOSED_HTTP_PORT` | Set to custom HTTP port if your Docker container runs on custom port. | 8080 | +| `CC_DOCKER_EXPOSED_TCP_PORT` | Set to custom TCP port if your Docker container runs on custom port. | 4040 | +| `CC_DOCKER_FIXED_CIDR_V6` | Activate the support of IPv6 with an IPv6 subnet int the docker daemon. | | +| `CC_DOCKER_LOGIN_PASSWORD` | The password of your username. | | +| `CC_DOCKER_LOGIN_SERVER` | The server of your private registry (optional). | Docker’s public registry | +| `CC_DOCKER_LOGIN_USERNAME` | The username to login to a private registry. | | +| `CC_MOUNT_DOCKER_SOCKET` | Set to true to access the host Docker socket from inside your container. | false | ## .NET [.NET Documentation](/doc/applications/dotnet) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`CC_DOTNET_PROFILE` | Override the build configuration settings in your project. | Release | -|`CC_DOTNET_PROJ` | The name of your project file to use for the build, without the .csproj / .fsproj / .vbproj extension. | | -|`CC_DOTNET_TFM` | Compiles for a specific framework. The framework must be defined in the project file. Example : `net5.0` | | -|`CC_DOTNET_VERSION` | Choose the .NET Core version between `6.0`,`8.0`, `9.0`. | 8.0 | -|`CC_RUN_COMMAND` | Custom command to run your application. | | +| Name | Description | Default value | +| ------------------- | -------------------------------------------------------------------------------------------------------- | ------------- | +| `CC_DOTNET_PROFILE` | Override the build configuration settings in your project. | Release | +| `CC_DOTNET_PROJ` | The name of your project file to use for the build, without the .csproj / .fsproj / .vbproj extension. | | +| `CC_DOTNET_TFM` | Compiles for a specific framework. The framework must be defined in the project file. Example : `net5.0` | | +| `CC_DOTNET_VERSION` | Choose the .NET Core version between `6.0`,`8.0`, `9.0`. | 8.0 | +| `CC_RUN_COMMAND` | Custom command to run your application. | | ## Elixir [Elixir Documentation](/doc/applications/elixir) - | Name | Description | Default value | - |-----------------------|------------------------------|--------------------------------| - |`CC_ELIXIR_VERSION` | Choose the Elixir version among [those supported](/doc/applications/elixir/#mandatory-configuration) | | - |`CC_MIX_BUILD_GOAL` | The mix goal to build the application (default compile) | | - |`CC_PHOENIX_ASSETS_DIR` | Folder in which your Phoenix assets are located. | | - |`CC_PHOENIX_DIGEST_GOAL` | Phoenix digest goal. | phx.digest | - |`CC_PHOENIX_RUN_ECTO_MIGRATE` | Whether to run 'mix ecto.migrate' or not. | true | - |`CC_PHOENIX_SERVER_GOAL` | Phoenix server goal. | phx.server | - |`CC_RUN_COMMAND` | Custom command to run your application. Replaces the default behaviour. | | + | Name | Description | Default value | + | ----------------------------- | ---------------------------------------------------------------------------------------------------- | ------------- | + | `CC_ELIXIR_VERSION` | Choose the Elixir version among [those supported](/doc/applications/elixir/#mandatory-configuration) | | + | `CC_MIX_BUILD_GOAL` | The mix goal to build the application (default compile) | | + | `CC_PHOENIX_ASSETS_DIR` | Folder in which your Phoenix assets are located. | | + | `CC_PHOENIX_DIGEST_GOAL` | Phoenix digest goal. | phx.digest | + | `CC_PHOENIX_RUN_ECTO_MIGRATE` | Whether to run 'mix ecto.migrate' or not. | true | + | `CC_PHOENIX_SERVER_GOAL` | Phoenix server goal. | phx.server | + | `CC_RUN_COMMAND` | Custom command to run your application. Replaces the default behaviour. | | ## FrankenPHP [FrankenPHP Documentation](/doc/applications/frankenphp) -| Name | Description | Default value | -|---------------|-------------| ---------------| -| `CC_FRANKENPHP_PORT` | The port on which FrankenPHP listens for HTTP requests | `8080` | -| `CC_FRANKENPHP_WORKER` | Path to the worker script, relative to the root of your project (e.g. `/worker/script.php`) | | -| `CC_PHP_COMPOSER_FLAGS` | Flags to pass to Composer | `-n --no-dev --no-progress --no-scripts` | -| `CC_PHP_DEV_DEPENDENCIES` | Set to `install` to install PHP development dependencies during build | | -| `CC_WEBROOT` | Path to the web content to serve, relative to the root of your application | `/` | +| Name | Description | Default value | +| ------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------- | +| `CC_FRANKENPHP_PORT` | The port on which FrankenPHP listens for HTTP requests | `8080` | +| `CC_FRANKENPHP_WORKER` | Path to the worker script, relative to the root of your project (e.g. `/worker/script.php`) | | +| `CC_PHP_COMPOSER_FLAGS` | Flags to pass to Composer | `-n --no-dev --no-progress --no-scripts` | +| `CC_PHP_DEV_DEPENDENCIES` | Set to `install` to install PHP development dependencies during build | | +| `CC_WEBROOT` | Path to the web content to serve, relative to the root of your application | `/` | ## Go [Go Documentation](/doc/applications/golang) -| Name | Description | -| :------- | :---- | +| Name | Description | +| :----------------- | :---------------------------------------------------------------------------------------------------------------- | | `CC_GO_BUILD_TOOL` | Available values: `gomod`, `gobuild`. Build and install your application. `goget` still exists but is deprecated. | -| `CC_GO_RUNDIR` | Run the application from the specified path, relative to `$GOPATH/src/`, now deprecated. | -| `CC_GO_PKG` | Tell the `CC_GO_BUILD_TOOL` which file contains the `main()` function, default `main.go`. | +| `CC_GO_RUNDIR` | Run the application from the specified path, relative to `$GOPATH/src/`, now deprecated. | +| `CC_GO_PKG` | Tell the `CC_GO_BUILD_TOOL` which file contains the `main()` function, default `main.go`. | ## Haskell [Haskell Documentation](/doc/applications/haskell) -| Name | Description | -|-----------------------|------------------------------| -|[`CC_HASKELL_STACK_TARGET`](/doc/applications/haskell/#specify-stack-package-target "Specify Stack package target") | Specify Stack package target. | -|`CC_HASKELL_STACK_SETUP_COMMAND` | Only use this variable to override the default `setup` Stack step command | -|`CC_HASKELL_STACK_INSTALL_COMMAND` | Only use this variable to override the default `install` Stack step command | -|`CC_HASKELL_STACK_INSTALL_DEPENDENCIES_COMMAND` | Only use this variable to override the default `install --only-dependencies` Stack step command | -|`CC_RUN_COMMAND` | Custom command to run your application. | +| Name | Description | +| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| [`CC_HASKELL_STACK_TARGET`](/doc/applications/haskell/#specify-stack-package-target "Specify Stack package target") | Specify Stack package target. | +| `CC_HASKELL_STACK_SETUP_COMMAND` | Only use this variable to override the default `setup` Stack step command | +| `CC_HASKELL_STACK_INSTALL_COMMAND` | Only use this variable to override the default `install` Stack step command | +| `CC_HASKELL_STACK_INSTALL_DEPENDENCIES_COMMAND` | Only use this variable to override the default `install --only-dependencies` Stack step command | +| `CC_RUN_COMMAND` | Custom command to run your application. | ## Java / Scala - [Java Documentation](/doc/applications/java) - [Scala Documentation](/doc/applications/scala) -| Name | Description | Default value | -|------|-------------|---------------| -|`CC_DISABLE_MAX_METASPACE` | Allows to disable the Java option `-XX:MaxMetaspaceSize` | | -|`CC_EXTRA_JAVA_ARGS` | Define extra arguments to pass to `java` for JAR | | -|`CC_JAR_ARGS` | Define arguments to pass to the launched JAR | | -|`CC_JAR_PATH` | Define the path to your JAR | | -|`CC_JAVA_VERSION` | Choose the JVM version between for OpenJDK or `graalvm-ce` for GraalVM | 21 | -|`CC_MAVEN_PROFILES` | Define which Maven profile to use during default build | | -|`CC_RUN_COMMAND` | Custom command to run your application. Replaces the default behavior | | -|`CC_SBT_TARGET_BIN` | Define the bin to pick in the `CC_SBT_TARGET_DIR`. The first bin found in the `CC_SBT_TARGET_DIR` | -|`CC_SBT_TARGET_DIR` | Define the folder the `target` dir is in. `/target/universal/stage/bin` is concatenated to the given directory | `.` | -|`GRADLE_DEPLOY_GOAL` | Define which Gradle goals to run during build | | -|`MAVEN_DEPLOY_GOAL` | Define which Maven goals to run during build | | -|`NUDGE_APPID` | | | -|`PLAY1_VERSION` | Define which play1 version to use between `1.2`, `1.3`, `1.4` and `1.5` | | -|`SBT_DEPLOY_GOAL` | Define which SBT goals to run during build | stage | +| Name | Description | Default value | +| -------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------- | +| `CC_DISABLE_MAX_METASPACE` | Allows to disable the Java option `-XX:MaxMetaspaceSize` | | +| `CC_EXTRA_JAVA_ARGS` | Define extra arguments to pass to `java` for JAR | | +| `CC_JAR_ARGS` | Define arguments to pass to the launched JAR | | +| `CC_JAR_PATH` | Define the path to your JAR | | +| `CC_JAVA_VERSION` | Choose the JVM version between for OpenJDK or `graalvm-ce` for GraalVM | 21 | +| `CC_MAVEN_PROFILES` | Define which Maven profile to use during default build | | +| `CC_RUN_COMMAND` | Custom command to run your application. Replaces the default behavior | | +| `CC_SBT_TARGET_BIN` | Define the bin to pick in the `CC_SBT_TARGET_DIR`. The first bin found in the `CC_SBT_TARGET_DIR` | | +| `CC_SBT_TARGET_DIR` | Define the folder the `target` dir is in. `/target/universal/stage/bin` is concatenated to the given directory | `.` | +| `GRADLE_DEPLOY_GOAL` | Define which Gradle goals to run during build | | +| `MAVEN_DEPLOY_GOAL` | Define which Maven goals to run during build | | +| `NUDGE_APPID` | | | +| `PLAY1_VERSION` | Define which play1 version to use between `1.2`, `1.3`, `1.4` and `1.5` | | +| `SBT_DEPLOY_GOAL` | Define which SBT goals to run during build | stage | ## Linux [Linux Documentation](/doc/applications/linux) -| Name | Description | Default value | -|------|-------------|---------------| -| `CC_BUILD_COMMAND` | The command to run during the build phase | `` | -| `CC_RUN_COMMAND` | The command to run during the run phase | `` | +| Name | Description | Default value | +| ------------------ | ----------------------------------------- | ------------- | +| `CC_BUILD_COMMAND` | The command to run during the build phase | `` | +| `CC_RUN_COMMAND` | The command to run during the run phase | `` | Use Linux runtime with [Mise package manager](#install-tools-with-mise-package-manager) to install your dependencies easily @@ -259,92 +259,92 @@ Use Linux runtime with [Mise package manager](#install-tools-with-mise-package-m [Node.js Documentation](/doc/applications/nodejs) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|[`CC_NODE_VERSION`](/doc/applications/nodejs#set-nodejs-version)| Set Node.js version, for example `24`, `23.11` or `22.15.1` | | -|`CC_NODE_DEV_DEPENDENCIES` | Control if development dependencies are installed or not. Values are either `install` or `ignore` | `ignore` | -|`CC_RUN_COMMAND` | Define a custom command. Example for Meteor: `node .build/bundle/main.js ` | | -|`CC_NODE_BUILD_TOOL` | Choose your build tool between bun, npm, npm-ci, pnpm, yarn, yarn2, yarn-berry and custom | npm | -|`CC_CUSTOM_BUILD_TOOL`| A custom command to run (with `CC_NODE_BUILD_TOOL` set to `custom`) | | -|`CC_NPM_REGISTRY` | The host of your private repository, available values: `github` or the registry host. | registry.npmjs.org | -|`CC_NPM_BASIC_AUTH`| Private repository credentials, in the form `user:password`. You can't use this if `CC_NPM_TOKEN` is set | | -|`NPM_TOKEN` | Private repository token | | +| Name | Description | Default value | +| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------ | +| [`CC_NODE_VERSION`](/doc/applications/nodejs#set-nodejs-version) | Set Node.js version, for example `24`, `23.11` or `22.15.1` | | +| `CC_NODE_DEV_DEPENDENCIES` | Control if development dependencies are installed or not. Values are either `install` or `ignore` | `ignore` | +| `CC_RUN_COMMAND` | Define a custom command. Example for Meteor: `node .build/bundle/main.js ` | | +| `CC_NODE_BUILD_TOOL` | Choose your build tool between bun, npm, npm-ci, pnpm, yarn, yarn2, yarn-berry and custom | npm | +| `CC_CUSTOM_BUILD_TOOL` | A custom command to run (with `CC_NODE_BUILD_TOOL` set to `custom`) | | +| `CC_NPM_REGISTRY` | The host of your private repository, available values: `github` or the registry host. | registry.npmjs.org | +| `CC_NPM_BASIC_AUTH` | Private repository credentials, in the form `user:password`. You can't use this if `CC_NPM_TOKEN` is set | | +| `NPM_TOKEN` | Private repository token | | ## PHP [PHP Documentation](/doc/applications/php) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`ALWAYS_POPULATE_RAW_POST_DATA` | | | -|`CC_COMPOSER_VERSION` | Choose your composer version between `2` or `lts` | 2 | -|[`CC_CONFIGURATION_PM_MAX_CHILDREN`](/doc/applications/php/#memory-limit) | Set the number of PHP-FPM workers. On XS and larger flavors, increasing it can lower the automatically calculated memory limit. Nano always uses six workers | Depends on the scaler size | -|[`CC_CGI_IMPLEMENTATION`](/doc/applications/php/apache/#change-the-fastcgi-module) | Choose the Apache FastCGI module between `fastcgi` and `proxy_fcgi` | proxy_fcgi | -|`CC_HTTP_BASIC_AUTH` | Restrict HTTP access to your application. Example: `login:password`. You can define multiple credentials using additional `CC_HTTP_BASIC_AUTH_n` (where `n` is a number) environment variables. | | -| `CC_APACHE_HEADERS_SIZE` | Set the maximum size of the headers in Apache, between `8` and `256`. Effective value depends on deployment region. [Ask for a dedicated load balancer](https://console.clever-cloud.com/ticket-center-choice) for a specific value | 8 | -|`CC_LDAP_CA_CERT` | | | -|`CC_MTA_AUTH_PASSWORD` | Password to authenticate to the SMTP server | | -|`CC_MTA_AUTH_USER` | User to authenticate to the SMTP server | | -|`CC_MTA_SERVER_AUTH_METHOD` | Enable or disable authentication to the SMTP server | on | -|`CC_MTA_SERVER_HOST` | Host of the SMTP server | | -|`CC_MTA_SERVER_PORT` | Port of the SMTP server | 465 | -|`CC_MTA_SERVER_USE_TLS` | Enable or disable TLS when connecting to the SMTP server | true | -|`CC_OPCACHE_INTERNED_STRINGS_BUFFER` | The amount of memory used to store interned strings, in megabytes. | Default 4 (PHP5), 8 (PHP7) | -|`CC_OPCACHE_MAX_ACCELERATED_FILES` | Maximum number of files handled by opcache. | Default depends on the scaler size | -|`CC_OPCACHE_MEMORY` | Set the shared opcache memory size | Default is about 1/8 of the RAM | -|`CC_OPCACHE_PRELOAD` | The path of the PHP preload file (PHP version 7.4 or higher). | | -|[`CC_PHP_ASYNC_APP_BUCKET`](/doc/applications/php/sessions-emails/#speed-up-or-disable-the-session-fs-bucket "Speed up or disable the session on FS Bucket") | Mount the default app FS bucket asynchronously. If set, should have value `async` | | -|[`CC_PHP_DEV_DEPENDENCIES`](/doc/applications/php/composer/#development-dependencies "Development dependencies") | Set to `install` to include dev dependencies; unset or set to `skip` to exclude them | | -|[`CC_PHP_DISABLE_APP_BUCKET`](/doc/applications/php/sessions-emails/#speed-up-or-disable-the-session-fs-bucket "Speed up or disable the session on FS Bucket") | Disable entirely the app FS Bucket. Values are either `true`, `yes` or `disable` | | -|`CC_PHP_VERSION` | Choose your PHP version among [those supported](/doc/applications/php/#php-version) | 8.3 | -|`CC_REALPATH_CACHE_TTL` | The size of the realpath cache to be used by PHP | 120 | -|`CC_WEBROOT` | Define the `DocumentRoot` of your project | `.` | -|`ENABLE_ELASTIC_APM_AGENT` | Elastic APM Agent for PHP | `true` if `ELASTIC_APM_SERVER_URL` is defined, `false` otherwise | -|`ENABLE_GRPC` | Enable the use of gRPC module | false | -|`ENABLE_PDFLIB` | Enable the use of PDFlib module | false | -|`ENABLE_REDIS` | | false | -|`HTTP_TIMEOUT` | Define a custom HTTP timeout | 180 | -|`LDAPTLS_CACERT` | | | -|`MAX_INPUT_VARS` | | | -|[`MEMORY_LIMIT`](/doc/applications/php/#memory-limit) | Set the maximum memory each PHP script can allocate, as a positive integer interpreted in MiB without a unit suffix | Depends on the scaler size | -|[`SESSION_TYPE`](/doc/applications/php/sessions-emails/#use-materia-kv-or-redis-to-store-php-sessions "Use Materia KV or Redis to store PHP sessions") | Choose `redis` to use it as session store | | -|`SOCKSIFY_EVERYTHING` | | | -|`SQREEN_API_APP_NAME` | The name of your sqreen application. | | -|`SQREEN_API_TOKEN` | organisation token. | | +| Name | Description | Default value | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| `ALWAYS_POPULATE_RAW_POST_DATA` | | | +| `CC_COMPOSER_VERSION` | Choose your composer version between `2` or `lts` | 2 | +| [`CC_CONFIGURATION_PM_MAX_CHILDREN`](/doc/applications/php/#memory-limit) | Set the number of PHP-FPM workers. On XS and larger flavors, increasing it can lower the automatically calculated memory limit. Nano always uses six workers | Depends on the scaler size | +| [`CC_CGI_IMPLEMENTATION`](/doc/applications/php/apache/#change-the-fastcgi-module) | Choose the Apache FastCGI module between `fastcgi` and `proxy_fcgi` | proxy_fcgi | +| `CC_HTTP_BASIC_AUTH` | Restrict HTTP access to your application. Example: `login:password`. You can define multiple credentials using additional `CC_HTTP_BASIC_AUTH_n` (where `n` is a number) environment variables. | | +| `CC_APACHE_HEADERS_SIZE` | Set the maximum size of the headers in Apache, between `8` and `256`. Effective value depends on deployment region. [Ask for a dedicated load balancer](https://console.clever-cloud.com/ticket-center-choice) for a specific value | 8 | +| `CC_LDAP_CA_CERT` | | | +| `CC_MTA_AUTH_PASSWORD` | Password to authenticate to the SMTP server | | +| `CC_MTA_AUTH_USER` | User to authenticate to the SMTP server | | +| `CC_MTA_SERVER_AUTH_METHOD` | Enable or disable authentication to the SMTP server | on | +| `CC_MTA_SERVER_HOST` | Host of the SMTP server | | +| `CC_MTA_SERVER_PORT` | Port of the SMTP server | 465 | +| `CC_MTA_SERVER_USE_TLS` | Enable or disable TLS when connecting to the SMTP server | true | +| `CC_OPCACHE_INTERNED_STRINGS_BUFFER` | The amount of memory used to store interned strings, in megabytes. | Default 4 (PHP5), 8 (PHP7) | +| `CC_OPCACHE_MAX_ACCELERATED_FILES` | Maximum number of files handled by opcache. | Default depends on the scaler size | +| `CC_OPCACHE_MEMORY` | Set the shared opcache memory size | Default is about 1/8 of the RAM | +| `CC_OPCACHE_PRELOAD` | The path of the PHP preload file (PHP version 7.4 or higher). | | +| [`CC_PHP_ASYNC_APP_BUCKET`](/doc/applications/php/sessions-emails/#speed-up-or-disable-the-session-fs-bucket "Speed up or disable the session on FS Bucket") | Mount the default app FS bucket asynchronously. If set, should have value `async` | | +| [`CC_PHP_DEV_DEPENDENCIES`](/doc/applications/php/composer/#development-dependencies "Development dependencies") | Set to `install` to include dev dependencies; unset or set to `skip` to exclude them | | +| [`CC_PHP_DISABLE_APP_BUCKET`](/doc/applications/php/sessions-emails/#speed-up-or-disable-the-session-fs-bucket "Speed up or disable the session on FS Bucket") | Disable entirely the app FS Bucket. Values are either `true`, `yes` or `disable` | | +| `CC_PHP_VERSION` | Choose your PHP version among [those supported](/doc/applications/php/#php-version) | 8.3 | +| `CC_REALPATH_CACHE_TTL` | The size of the realpath cache to be used by PHP | 120 | +| `CC_WEBROOT` | Define the `DocumentRoot` of your project | `.` | +| `ENABLE_ELASTIC_APM_AGENT` | Elastic APM Agent for PHP | `true` if `ELASTIC_APM_SERVER_URL` is defined, `false` otherwise | +| `ENABLE_GRPC` | Enable the use of gRPC module | false | +| `ENABLE_PDFLIB` | Enable the use of PDFlib module | false | +| `ENABLE_REDIS` | | false | +| `HTTP_TIMEOUT` | Define a custom HTTP timeout | 180 | +| `LDAPTLS_CACERT` | | | +| `MAX_INPUT_VARS` | | | +| [`MEMORY_LIMIT`](/doc/applications/php/#memory-limit) | Set the maximum memory each PHP script can allocate, as a positive integer interpreted in MiB without a unit suffix | Depends on the scaler size | +| [`SESSION_TYPE`](/doc/applications/php/sessions-emails/#use-materia-kv-or-redis-to-store-php-sessions "Use Materia KV or Redis to store PHP sessions") | Choose `redis` to use it as session store | | +| `SOCKSIFY_EVERYTHING` | | | +| `SQREEN_API_APP_NAME` | The name of your sqreen application. | | +| `SQREEN_API_TOKEN` | organisation token. | | ## Python [Python Documentation](/doc/applications/python) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`CC_HTTP_BASIC_AUTH` | Restrict HTTP access to your application. Example: `login:password`. You can define multiple credentials using additional `CC_HTTP_BASIC_AUTH_n` (where `n` is a number) environment variables. | | -|[`CC_NGINX_PROXY_BUFFERS`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers "Nginx documentation") | Sets the number and size of the buffers used for reading a response from the proxied server, for a single connection. | | -|[`CC_NGINX_PROXY_BUFFER_SIZE`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size "Nginx documentation") | Sets the size of the buffer used for reading the first part of the response received from the proxied server.| | -|`CC_PIP_REQUIREMENTS_FILE` | Allows you to define a custom requirements.txt file | requirements.txt | -|`CC_PYTHON_BACKEND` | Choose the Python backend to use between daphne, gunicorn, uvicorn and uwsgi | uwsgi | -|`CC_PYTHON_CELERY_LOGFILE` | Relative path to your Celery logfile: /path/to/logdir | | -|`CC_PYTHON_CELERY_MODULE` | Specify the Celery module you want to start | | -|`CC_PYTHON_CELERY_USE_BEAT` | Set to "true" to activate Beat support | | -|[`CC_PYTHON_MANAGE_TASKS`](/guides/python-django-sample/#manage-py-tasks) | Comma-separated list of Django manage tasks | | -|`CC_PYTHON_MODULE` | Select which module you want to start with the path to the folder containing the app object. For example, a module called **server.py** in a folder called **/app** would be used here as **app.server:app** | | -|`CC_PYTHON_USE_GEVENT` | Set to true to enable Gevent | | -|`CC_PYTHON_VERSION` | Choose the Python version among [those supported](/doc/applications/python/#python-version) | 3 | -|`ENABLE_GZIP_COMPRESSION` | Set to `true` to gzip-compress through Nginx | | -|`GZIP_TYPES` | Set the mime types to compress. | text/plain text/css text/xml text/javascript application/json application/xml application/javascript image/svg+xml | -|`HARAKIRI` | Timeout (in seconds) after which an unresponding process is killed | 180 | -|`NGINX_READ_TIMEOUT` | Read timeout in seconds | 300 | -|`PYTHON_SETUP_PY_GOAL` | Custom setup goal to be launch after requirements.txt have been installed | | -|`STATIC_FILES_PATH` | Relative path to where your static files are stored: path/to/static | | -|[`STATIC_URL_PREFIX`](/doc/applications/python/#configure-your-python-application "Configure your Python application") | The URL path under which you want to serve static file, usually /public | | -|`STATIC_WEBROOT` | | | -|[`UWSGI_ASYNC`](/doc/applications/python/#configure-your-python-application "Configure your Python application" ) | Number of cores to use for uWSGI asynchronous/non-blocking modes | | -|`UWSGI_ASYNC_ENGINE` | Select the asynchronous engine for uWSGI (optional) | | -|`UWSGI_INTERCEPT_ERRORS` | | -|`WSGI_BUFFER_SIZE` | Buffer size (in bytes) for uploads. | 4096 | -|`WSGI_POST_BUFFERING` | Maximal size (in bytes) for the headers of a request. | 4096 | -|`WSGI_THREADS` | Number of threads per worker. (Default: automatically setup with the scaler size) | | -|`WSGI_WORKERS` | Number of workers. (Default: automatically setup with the scaler size) | | +| Name | Description | Default value | +| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `CC_HTTP_BASIC_AUTH` | Restrict HTTP access to your application. Example: `login:password`. You can define multiple credentials using additional `CC_HTTP_BASIC_AUTH_n` (where `n` is a number) environment variables. | | +| [`CC_NGINX_PROXY_BUFFERS`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers "Nginx documentation") | Sets the number and size of the buffers used for reading a response from the proxied server, for a single connection. | | +| [`CC_NGINX_PROXY_BUFFER_SIZE`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size "Nginx documentation") | Sets the size of the buffer used for reading the first part of the response received from the proxied server. | | +| `CC_PIP_REQUIREMENTS_FILE` | Allows you to define a custom requirements.txt file | requirements.txt | +| `CC_PYTHON_BACKEND` | Choose the Python backend to use between daphne, gunicorn, uvicorn and uwsgi | uwsgi | +| `CC_PYTHON_CELERY_LOGFILE` | Relative path to your Celery logfile: /path/to/logdir | | +| `CC_PYTHON_CELERY_MODULE` | Specify the Celery module you want to start | | +| `CC_PYTHON_CELERY_USE_BEAT` | Set to "true" to activate Beat support | | +| [`CC_PYTHON_MANAGE_TASKS`](/guides/python-django-sample/#manage-py-tasks) | Comma-separated list of Django manage tasks | | +| `CC_PYTHON_MODULE` | Select which module you want to start with the path to the folder containing the app object. For example, a module called **server.py** in a folder called **/app** would be used here as **app.server:app** | | +| `CC_PYTHON_USE_GEVENT` | Set to true to enable Gevent | | +| `CC_PYTHON_VERSION` | Choose the Python version among [those supported](/doc/applications/python/#python-version) | 3 | +| `ENABLE_GZIP_COMPRESSION` | Set to `true` to gzip-compress through Nginx | | +| `GZIP_TYPES` | Set the mime types to compress. | text/plain text/css text/xml text/javascript application/json application/xml application/javascript image/svg+xml | +| `HARAKIRI` | Timeout (in seconds) after which an unresponding process is killed | 180 | +| `NGINX_READ_TIMEOUT` | Read timeout in seconds | 300 | +| `PYTHON_SETUP_PY_GOAL` | Custom setup goal to be launch after requirements.txt have been installed | | +| `STATIC_FILES_PATH` | Relative path to where your static files are stored: path/to/static | | +| [`STATIC_URL_PREFIX`](/doc/applications/python/#configure-your-python-application "Configure your Python application") | The URL path under which you want to serve static file, usually /public | | +| `STATIC_WEBROOT` | | | +| [`UWSGI_ASYNC`](/doc/applications/python/#configure-your-python-application "Configure your Python application" ) | Number of cores to use for uWSGI asynchronous/non-blocking modes | | +| `UWSGI_ASYNC_ENGINE` | Select the asynchronous engine for uWSGI (optional) | | +| `UWSGI_INTERCEPT_ERRORS` | | | +| `WSGI_BUFFER_SIZE` | Buffer size (in bytes) for uploads. | 4096 | +| `WSGI_POST_BUFFERING` | Maximal size (in bytes) for the headers of a request. | 4096 | +| `WSGI_THREADS` | Number of threads per worker. (Default: automatically setup with the scaler size) | | +| `WSGI_WORKERS` | Number of workers. (Default: automatically setup with the scaler size) | | When your Python application doesn't use one of the supported backends, with `CC_RUN_COMMAND` for example, it must listen on port `9000`, not `8080`. @@ -353,51 +353,51 @@ When your Python application doesn't use one of the supported backends, with `CC - [Ruby Guide](/guides/ruby-rack-app-tutorial) - [Ruby Documentation](/doc/applications/ruby) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|[`CC_ENABLE_SIDEKIQ`](/doc/applications/ruby/#configure-sidekiq) | Enable Sidekiq background process | false | -|`CC_HTTP_BASIC_AUTH` | Restrict HTTP access to your application. Example: `login:password`. You can define multiple credentials using additional `CC_HTTP_BASIC_AUTH_n` (where `n` is a number) environment variables. | | -|`CC_NGINX_PROXY_BUFFERS` | Sets the number and size of the buffers used for reading a response from the proxied server, for a single connection. [Nginx documentation](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers "Nginx documentation") | | -|`CC_NGINX_PROXY_BUFFER_SIZE` | Sets the size of the buffer used for reading the first part of the response received from the proxied server. [Nginx documentation](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size "Nginx documentation") | | -|`CC_RACKUP_SERVER` | The server to use for serving the ruby application | puma | | -|[`CC_RAKEGOALS`](/doc/applications/ruby/#configure-rake-goals) | A list of comma-separated rake goals to execute e.g. db:migrate, assets:precompile | | -|`CC_RUBY_VERSION` | Choose the Ruby version to use but we strongly advise to set Ruby version in your Gemfile | | | -|`CC_SIDEKIQ_FILES` | Specify a list of Sidekiq configuration files e.g. "./config/sidekiq_1.yml,./config/sidekiq_2.yml" | | -|`ENABLE_GZIP_COMPRESSION` | Set to `true` to gzip-compress through Nginx | | -|`GZIP_TYPES` | Set the mime types to compress. | text/plain text/css text/xml text/javascript application/json application/xml application/javascript image/svg+xml | -|`NGINX_READ_TIMEOUT` | Read timeout in seconds | 300 | -|`RACK_ENV` | | | -|`RAILS_ENV` | | | | -|`STATIC_FILES_PATH` | Relative path to where your static files are stored: path/to/static | | -|[`STATIC_URL_PREFIX`](/doc/applications/ruby/#manage-your-static-files-and-assets) | The URL path under which you want to serve static file, usually /public | | -|`STATIC_WEBROOT` | | | +| Name | Description | Default value | +| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| [`CC_ENABLE_SIDEKIQ`](/doc/applications/ruby/#configure-sidekiq) | Enable Sidekiq background process | false | +| `CC_HTTP_BASIC_AUTH` | Restrict HTTP access to your application. Example: `login:password`. You can define multiple credentials using additional `CC_HTTP_BASIC_AUTH_n` (where `n` is a number) environment variables. | | +| `CC_NGINX_PROXY_BUFFERS` | Sets the number and size of the buffers used for reading a response from the proxied server, for a single connection. [Nginx documentation](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers "Nginx documentation") | | +| `CC_NGINX_PROXY_BUFFER_SIZE` | Sets the size of the buffer used for reading the first part of the response received from the proxied server. [Nginx documentation](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size "Nginx documentation") | | +| `CC_RACKUP_SERVER` | The server to use for serving the ruby application | puma | +| [`CC_RAKEGOALS`](/doc/applications/ruby/#configure-rake-goals) | A list of comma-separated rake goals to execute e.g. db:migrate, assets:precompile | | +| `CC_RUBY_VERSION` | Choose the Ruby version to use but we strongly advise to set Ruby version in your Gemfile | | +| `CC_SIDEKIQ_FILES` | Specify a list of Sidekiq configuration files e.g. "./config/sidekiq_1.yml,./config/sidekiq_2.yml" | | +| `ENABLE_GZIP_COMPRESSION` | Set to `true` to gzip-compress through Nginx | | +| `GZIP_TYPES` | Set the mime types to compress. | text/plain text/css text/xml text/javascript application/json application/xml application/javascript image/svg+xml | +| `NGINX_READ_TIMEOUT` | Read timeout in seconds | 300 | +| `RACK_ENV` | | | +| `RAILS_ENV` | | | +| `STATIC_FILES_PATH` | Relative path to where your static files are stored: path/to/static | | +| [`STATIC_URL_PREFIX`](/doc/applications/ruby/#manage-your-static-files-and-assets) | The URL path under which you want to serve static file, usually /public | | +| `STATIC_WEBROOT` | | | ## Rust [Rust Documentation](/doc/applications/rust) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`CC_RUSTUP_CHANNEL` | The rust channel to use. Use a specific channel version with stable, beta, nightly or a specific version like 1.13.0 | stable | -|`CC_RUST_BIN` | The name of the binary to launch once built | | -|`CC_RUST_FEATURES` | The list of features to enable | | -|`CC_RUN_COMMAND` | Custom command to run your application. | | +| Name | Description | Default value | +| ------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------- | +| `CC_RUSTUP_CHANNEL` | The rust channel to use. Use a specific channel version with stable, beta, nightly or a specific version like 1.13.0 | stable | +| `CC_RUST_BIN` | The name of the binary to launch once built | | +| `CC_RUST_FEATURES` | The list of features to enable | | +| `CC_RUN_COMMAND` | Custom command to run your application. | | ## Static [Static Documentation](/doc/applications/static) -| Name | Description | Default value | -|------|-------------|---------------| -| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.161` | | -| `CC_BUILD_COMMAND` | The command to run during the build phase | | -| `CC_OVERRIDE_BUILDCACHE` | Files and path to put in the build cache, separated by a `:` | | +| Name | Description | Default value | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------- | +| `CC_HUGO_VERSION` | Set the Hugo version, for example `0.161` | | +| `CC_BUILD_COMMAND` | The command to run during the build phase | | +| `CC_OVERRIDE_BUILDCACHE` | Files and path to put in the build cache, separated by a `:` | | | `CC_STATIC_AUTOBUILD_OUTDIR` | The output directory of the static site generator (SSG) auto-build, relative to the root of your application | `/cc_static_autobuilt` | -| `CC_STATIC_CADDYFILE` | Path to the Caddyfile, as expected by `caddy run --config` command | `./Caddyfile` | -| `CC_STATIC_FLAGS` | Command line flags to pass to the static web server | | -| `CC_STATIC_PORT` | The port on which the static web server listens for HTTP requests | `8080` | -| `CC_STATIC_SERVER` | The server to use to serve your static website, can be `caddy` | `static-web-server` | -| `CC_WEBROOT` | Path to the web content to serve, relative to the root of your application, used as default build cache | `/` | +| `CC_STATIC_CADDYFILE` | Path to the Caddyfile, as expected by `caddy run --config` command | `./Caddyfile` | +| `CC_STATIC_FLAGS` | Command line flags to pass to the static web server | | +| `CC_STATIC_PORT` | The port on which the static web server listens for HTTP requests | `8080` | +| `CC_STATIC_SERVER` | The server to use to serve your static website, can be `caddy` | `static-web-server` | +| `CC_WEBROOT` | Path to the web content to serve, relative to the root of your application, used as default build cache | `/` | > [!NOTE] Static Site Generators (SSG) Auto-build > If no `CC_BUILD_COMMAND` is provided, Clever Cloud tries to detect and configure Static Site Generator (SSG), any provided `CC_WEBROOT` is ignored. See [Static Site Generators (SSG) Auto-build](/doc/applications/static/#static-site-generators-ssg-auto-build) for more information. @@ -406,10 +406,10 @@ When your Python application doesn't use one of the supported backends, with `CC [V (Vlang)](/doc/applications/v) -| Name | Description | Default value | -|------|-------------|---------------| -| `CC_V_BINARY` | The name of the output binary file. | `${APP_HOME}/v_bin_${APP_ID}` | -| `ENVIRONMENT` | Set to `development` to compile without the `-prod` flag | | +| Name | Description | Default value | +| ------------- | -------------------------------------------------------- | ----------------------------- | +| `CC_V_BINARY` | The name of the output binary file. | `${APP_HOME}/v_bin_${APP_ID}` | +| `ENVIRONMENT` | Set to `development` to compile without the `-prod` flag | | ## Add-ons environment variables @@ -417,196 +417,196 @@ When your Python application doesn't use one of the supported backends, with `CC [FS Bucket Documentation](/doc/addons/fs-bucket) -| Name | Description | Read Only | -|-----------------------|------------------------------|--------------------------------| -|`BUCKET_HOST` | Hostname of the bucket | ✓ | -|`CC_FS_BUCKET` | Defines which bucket to mount on which path | | +| Name | Description | Read Only | +| -------------- | ------------------------------------------- | --------- | +| `BUCKET_HOST` | Hostname of the bucket | ✓ | +| `CC_FS_BUCKET` | Defines which bucket to mount on which path | | ### MongoDB [MongoDB Documentation](/doc/addons/mongodb) -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|`MONGODB_ADDON_HOST` | | Generated upon creation | ✓ | -|`MONGODB_ADDON_PORT` | | Generated upon creation | ✓ | -|`MONGODB_ADDON_DIRECT_HOST` | Points directly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | -|`MONGODB_ADDON_DIRECT_PORT` | Points to ectly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | -|`MONGODB_ADDON_DB` | | Generated upon creation | ✓ | -|`MONGODB_ADDON_PASSWORD` | | Generated upon creation | ✓ | -|`MONGODB_ADDON_USER` | | Generated upon creation | ✓ | +| Name | Description | Default value | Read Only | +| --------------------------- | ---------------------------------------------------- | ------------------------------------- | --------- | +| `MONGODB_ADDON_HOST` | | Generated upon creation | ✓ | +| `MONGODB_ADDON_PORT` | | Generated upon creation | ✓ | +| `MONGODB_ADDON_DIRECT_HOST` | Points directly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | +| `MONGODB_ADDON_DIRECT_PORT` | Points to ectly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | +| `MONGODB_ADDON_DB` | | Generated upon creation | ✓ | +| `MONGODB_ADDON_PASSWORD` | | Generated upon creation | ✓ | +| `MONGODB_ADDON_USER` | | Generated upon creation | ✓ | ### MySQL [MySQL Documentation](/doc/addons/mysql) -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|`MYSQL_ADDON_HOST` | | Generated upon creation | ✓ | -|`MYSQL_ADDON_PORT` | | Generated upon creation | ✓ | -|`MYSQL_ADDON_DIRECT_HOST` | Points directly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | -|`MYSQL_ADDON_DIRECT_PORT` | Points to ectly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | -|`MYSQL_ADDON_DB` | | Generated upon creation | ✓ | -|`MYSQL_ADDON_PASSWORD` | | Generated upon creation | ✓ | -|`MYSQL_ADDON_ROLE` | | Generated upon creation | ✓ | -|`MYSQL_ADDON_USER` | | Generated upon creation | ✓ | +| Name | Description | Default value | Read Only | +| ------------------------- | ---------------------------------------------------- | ------------------------------------- | --------- | +| `MYSQL_ADDON_HOST` | | Generated upon creation | ✓ | +| `MYSQL_ADDON_PORT` | | Generated upon creation | ✓ | +| `MYSQL_ADDON_DIRECT_HOST` | Points directly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | +| `MYSQL_ADDON_DIRECT_PORT` | Points to ectly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | +| `MYSQL_ADDON_DB` | | Generated upon creation | ✓ | +| `MYSQL_ADDON_PASSWORD` | | Generated upon creation | ✓ | +| `MYSQL_ADDON_ROLE` | | Generated upon creation | ✓ | +| `MYSQL_ADDON_USER` | | Generated upon creation | ✓ | ### ProxySQL [ProxySQL Documentation](/guides/proxysql) -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|`CC_ENABLE_MYSQL_PROXYSQL` | Enable the ProxySQL feature | false | | -|`CC_MYSQL_PROXYSQL_MAX_CONNECTIONS` | Defines the maximum number of connections the local ProxySQL will open to your MySQL add-on | 10 | | -|`CC_MYSQL_PROXYSQL_USE_TLS` | Enable or disable secured connection using TLS to your MySQL add-on | true | | -|`CC_MYSQL_PROXYSQL_SOCKET_PATH` | Contains the path to the Unix Datagram Socket to connect to ProxySQL | | ✓ | +| Name | Description | Default value | Read Only | +| ----------------------------------- | ------------------------------------------------------------------------------------------- | ------------- | --------- | +| `CC_ENABLE_MYSQL_PROXYSQL` | Enable the ProxySQL feature | false | | +| `CC_MYSQL_PROXYSQL_MAX_CONNECTIONS` | Defines the maximum number of connections the local ProxySQL will open to your MySQL add-on | 10 | | +| `CC_MYSQL_PROXYSQL_USE_TLS` | Enable or disable secured connection using TLS to your MySQL add-on | true | | +| `CC_MYSQL_PROXYSQL_SOCKET_PATH` | Contains the path to the Unix Datagram Socket to connect to ProxySQL | | ✓ | ### PostgreSQL [PostgreSQL Documentation](/doc/addons/postgresql) -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|`POSTGRESQL_ADDON_URI` | Complete URI to connect to the PostgreSQL database | Generated upon creation | ✓ | -|`POSTGRESQL_ADDON_HOST` | | Generated upon creation | ✓ | -|`POSTGRESQL_ADDON_PORT` | | Generated upon creation | ✓ | -|`POSTGRESQL_ADDON_DIRECT_HOST` | Points directly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | -|`POSTGRESQL_ADDON_DIRECT_PORT` | Points to ectly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | -|`POSTGRESQL_ADDON_DB` | | Generated upon creation | ✓ | -|`POSTGRESQL_ADDON_PASSWORD` | | Generated upon creation | ✓ | -|`POSTGRESQL_ADDON_ROLE` | | Generated upon creation | ✓ | -|`POSTGRESQL_ADDON_USER` | | Generated upon creation | ✓ | +| Name | Description | Default value | Read Only | +| ------------------------------ | ---------------------------------------------------- | ------------------------------------- | --------- | +| `POSTGRESQL_ADDON_URI` | Complete URI to connect to the PostgreSQL database | Generated upon creation | ✓ | +| `POSTGRESQL_ADDON_HOST` | | Generated upon creation | ✓ | +| `POSTGRESQL_ADDON_PORT` | | Generated upon creation | ✓ | +| `POSTGRESQL_ADDON_DIRECT_HOST` | Points directly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | +| `POSTGRESQL_ADDON_DIRECT_PORT` | Points to ectly to the VM, bypassing reverse proxies | Generated on demand via the dashboard | ✓ | +| `POSTGRESQL_ADDON_DB` | | Generated upon creation | ✓ | +| `POSTGRESQL_ADDON_PASSWORD` | | Generated upon creation | ✓ | +| `POSTGRESQL_ADDON_ROLE` | | Generated upon creation | ✓ | +| `POSTGRESQL_ADDON_USER` | | Generated upon creation | ✓ | ### Pgpool-II [Pgpool-II Documentation](/guides/pgpool) -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -|`CC_ENABLE_PGPOOL` | Enables the Pgpool-II feature | false | -|`CC_PGPOOL_SOCKET_PATH` | Contains the path to the Unix Datagram Socket to connect to Pgpool-II | true | -|`CC_PGPOOL_PCP_SOCKET_PATH` | Contains the path to the Unix Datagram Socket to connect to PCP | true | -|`CC_PGPOOL_EXTRA_USERS` | Add new user/password pairs to the pool_passwd file, separated by commas (user1:password,user2:password…) | ' ' | -|`CC_PGPOOL_RESERVED_CONNECTIONS` | Number of reserved connections | 0 | -|`CC_PGPOOL_LISTEN_BACKLOG_MULTIPLIER` | Specifies the length of connection queue from frontend to Pgpool-II | 2 | -|`CC_PGPOOL_LEADER_WEIGHT` | Weight for backend 0 (Leader) | 1 | -|`CC_PGPOOL_NUM_INIT_CHILDREN` | Number of concurrent sessions allowed | 16 | -|`CC_PGPOOL_MAX_POOL` | Number of connection pool caches per connection | 1 | -|`CC_PGPOOL_CHILD_LIFE_TIME` | Pool exits after being idle for this many seconds | 300 | -|`CC_PGPOOL_CHILD_MAX_CONNECTIONS` | Pool exits after receiving that many connections | 0 (no exit) | -|`CC_PGPOOL_CONNECTION_LIFE_TIME` | Connection to backend closes after being idle for this many seconds | 0 (no close) | -|`CC_PGPOOL_CLIENT_IDLE_LIMIT` | Client is disconnected after being idle for that many seconds (even inside an explicit transactions!) | 0 (no disconnection) | | -|`CC_PGPOOL_LOG_CONNECTIONS` | Log connections | off | -|`CC_PGPOOL_LOG_DISCONNECTIONS` | Log disconnections | off | -|`CC_PGPOOL_LOG_HOSTNAME` | Hostname will be shown in ps status and in logs if connections are logged | off | -|`CC_PGPOOL_LOG_STATEMENT` | Log all statements | off | -|`CC_PGPOOL_LOG_PER_NODE_STATEMENT` | Log all statements with node and backend informations | off | -|`CC_PGPOOL_LOG_CLIENT_MESSAGES` | Log any client messages | off | -|`CC_PGPOOL_LOG_STANDBY_DELAY` | Log standby delay. Valid values are combinations of always, if_over_threshold and none | if_over_threshold | -|`CC_PGPOOL_CONNECTION_CACHE` | Activate connection pools | on | -|`CC_PGPOOL_REPLICATE_SELECT` | Replicate SELECT statements in replication mode | off | -|`CC_PGPOOL_INSERT_LOCK` | Automatically locks a dummy row or a table with INSERT statements to keep SERIAL data consistency | on | -|`CC_PGPOOL_LOBJ_LOCK_TABLE` | When rewriting lo_creat command in replication mode, specify table name to lock | ' ' | -|`CC_PGPOOL_REPLICATION_STOP_ON_MISMATCH` | On disagreement with the packet kind sent from backend, degenerate the node which is most likely "minority" If off, just force to exit this session | off | -|`CC_PGPOOL_FAILOVER_IF_AFFECTED_TUPLES_MISMATCH` | On disagreement with the number of affected tuples in UPDATE/DELETE queries, then degenerate the node which is most likely "minority". If off, just abort the transaction to keep the consistency | off | -|`CC_PGPOOL_LOAD_BALANCE_MODE` | Activate load balancing mode | on | -|`CC_PGPOOL_IGNORE_LEADING_WHITE_SPACE` | Ignore leading white spaces of each query | on | -|`CC_PGPOOL_READ_ONLY_FUNCTION_LIST` | Comma separated list of function names that don't write to database (regexp are accepted) | ' ' | -|`CC_PGPOOL_WRITE_FUNCTION_LIST` | Comma separated list of function names that write to database (regexp are accepted) | ' ' | -|`CC_PGPOOL_PRIMARY_ROUTING_QUERY_PATTERN_LIST` | Semicolon separated list of query patterns that should be sent to primary node (regexp are accepted) | ' ' | -|`CC_PGPOOL_DATABASE_REDIRECT_PREFERENCE_LIST` | Comma separated list of pairs of database and node id | ' ' | -|`CC_PGPOOL_APP_NAME_REDIRECT_PREFERENCE_LIST` | Comma separated list of pairs of app name and node id | ' ' | -|`CC_PGPOOL_ALLOW_SQL_COMMENTS` | If on, ignore SQL comments when judging if load balance or query cache is possible | off | -|`CC_PGPOOL_DISABLE_LOAD_BALANCE_ON_WRITE` | Load balance behavior when write query is issued in an explicit transaction. Valid values are "transaction", "trans_transaction", "dml_adaptive" or "always" | transaction | -|`CC_PGPOOL_DML_ADAPTIVE_OBJECT_RELATIONSHIP_LIST` | Comma separated list of object pairs | ' ' | -|`CC_PGPOOL_STATEMENT_LEVEL_LOAD_BALANCE` | Enables statement level load balancing | off | -|`CC_PGPOOL_SR_CHECK_PERIOD` | Streaming replication check period | 10 | -|`CC_PGPOOL_DELAY_THRESHOLD` | Threshold before not dispatching query to standby node Unit is in bytes | 10000000 | -|`CC_PGPOOL_HEALTH_CHECK_PERIOD` | Health check period | 20 | -|`CC_PGPOOL_HEALTH_CHECK_TIMEOUT` | Health check timeout | 20 | -|`CC_PGPOOL_HEALTH_CHECK_MAX_RETRIES` | Maximum number of times to retry a failed health check before giving up | 0 | -|`CC_PGPOOL_HEALTH_CHECK_RETRY_DELAY` | Amount of time to wait (in seconds) between retries | 1 | -|`CC_PGPOOL_CONNECT_TIMEOUT` | Timeout value in milliseconds before giving up to connect to backend | 10000 | -|`CC_PGPOOL_MEMORY_CACHE_ENABLED` | Use the memory cache functionality | off | -|`CC_PGPOOL_MEMQCACHE_TOTAL_SIZE` | Total memory size in megabytes for storing memory cache | 64 | -|`CC_PGPOOL_MEMQCACHE_MAX_NUM_CACHE` | Total number of cache entries | 1000000 | -|`CC_PGPOOL_MEMQCACHE_EXPIRE` | Memory cache entry life time specified in seconds | 0 | -|`CC_PGPOOL_MEMQCACHE_AUTO_CACHE_INVALIDATION` | Invalidation of query cache is triggered by corresponding DDL/DML/DCL | on | -|`CC_PGPOOL_MEMQCACHE_MAXCACHE` | Maximum SELECT result size in kilobytes (must be smaller than MEMQCACHE_CACHE_BLOCK_SIZE) | 400 | -|`CC_PGPOOL_MEMQCACHE_CACHE_BLOCK_SIZE` | Cache block size in megabytes | 1 | -|`CC_PGPOOL_CACHE_SAFE_MEMQCACHE_TABLE_LIST` | Comma separated list of table names to memcache that don't write to database (regexp are accepted) | ' ' | -|`CC_PGPOOL_CACHE_UNSAFE_MEMQCACHE_TABLE_LIST` | Comma separated list of table names not to memcache that don't write to database (regexp are accepted) | ' ' | +| Name | Description | Default value | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| `CC_ENABLE_PGPOOL` | Enables the Pgpool-II feature | false | +| `CC_PGPOOL_SOCKET_PATH` | Contains the path to the Unix Datagram Socket to connect to Pgpool-II | true | +| `CC_PGPOOL_PCP_SOCKET_PATH` | Contains the path to the Unix Datagram Socket to connect to PCP | true | +| `CC_PGPOOL_EXTRA_USERS` | Add new user/password pairs to the pool_passwd file, separated by commas (user1:password,user2:password…) | ' ' | +| `CC_PGPOOL_RESERVED_CONNECTIONS` | Number of reserved connections | 0 | +| `CC_PGPOOL_LISTEN_BACKLOG_MULTIPLIER` | Specifies the length of connection queue from frontend to Pgpool-II | 2 | +| `CC_PGPOOL_LEADER_WEIGHT` | Weight for backend 0 (Leader) | 1 | +| `CC_PGPOOL_NUM_INIT_CHILDREN` | Number of concurrent sessions allowed | 16 | +| `CC_PGPOOL_MAX_POOL` | Number of connection pool caches per connection | 1 | +| `CC_PGPOOL_CHILD_LIFE_TIME` | Pool exits after being idle for this many seconds | 300 | +| `CC_PGPOOL_CHILD_MAX_CONNECTIONS` | Pool exits after receiving that many connections | 0 (no exit) | +| `CC_PGPOOL_CONNECTION_LIFE_TIME` | Connection to backend closes after being idle for this many seconds | 0 (no close) | +| `CC_PGPOOL_CLIENT_IDLE_LIMIT` | Client is disconnected after being idle for that many seconds (even inside an explicit transactions!) | 0 (no disconnection) | +| `CC_PGPOOL_LOG_CONNECTIONS` | Log connections | off | +| `CC_PGPOOL_LOG_DISCONNECTIONS` | Log disconnections | off | +| `CC_PGPOOL_LOG_HOSTNAME` | Hostname will be shown in ps status and in logs if connections are logged | off | +| `CC_PGPOOL_LOG_STATEMENT` | Log all statements | off | +| `CC_PGPOOL_LOG_PER_NODE_STATEMENT` | Log all statements with node and backend informations | off | +| `CC_PGPOOL_LOG_CLIENT_MESSAGES` | Log any client messages | off | +| `CC_PGPOOL_LOG_STANDBY_DELAY` | Log standby delay. Valid values are combinations of always, if_over_threshold and none | if_over_threshold | +| `CC_PGPOOL_CONNECTION_CACHE` | Activate connection pools | on | +| `CC_PGPOOL_REPLICATE_SELECT` | Replicate SELECT statements in replication mode | off | +| `CC_PGPOOL_INSERT_LOCK` | Automatically locks a dummy row or a table with INSERT statements to keep SERIAL data consistency | on | +| `CC_PGPOOL_LOBJ_LOCK_TABLE` | When rewriting lo_creat command in replication mode, specify table name to lock | ' ' | +| `CC_PGPOOL_REPLICATION_STOP_ON_MISMATCH` | On disagreement with the packet kind sent from backend, degenerate the node which is most likely "minority" If off, just force to exit this session | off | +| `CC_PGPOOL_FAILOVER_IF_AFFECTED_TUPLES_MISMATCH` | On disagreement with the number of affected tuples in UPDATE/DELETE queries, then degenerate the node which is most likely "minority". If off, just abort the transaction to keep the consistency | off | +| `CC_PGPOOL_LOAD_BALANCE_MODE` | Activate load balancing mode | on | +| `CC_PGPOOL_IGNORE_LEADING_WHITE_SPACE` | Ignore leading white spaces of each query | on | +| `CC_PGPOOL_READ_ONLY_FUNCTION_LIST` | Comma separated list of function names that don't write to database (regexp are accepted) | ' ' | +| `CC_PGPOOL_WRITE_FUNCTION_LIST` | Comma separated list of function names that write to database (regexp are accepted) | ' ' | +| `CC_PGPOOL_PRIMARY_ROUTING_QUERY_PATTERN_LIST` | Semicolon separated list of query patterns that should be sent to primary node (regexp are accepted) | ' ' | +| `CC_PGPOOL_DATABASE_REDIRECT_PREFERENCE_LIST` | Comma separated list of pairs of database and node id | ' ' | +| `CC_PGPOOL_APP_NAME_REDIRECT_PREFERENCE_LIST` | Comma separated list of pairs of app name and node id | ' ' | +| `CC_PGPOOL_ALLOW_SQL_COMMENTS` | If on, ignore SQL comments when judging if load balance or query cache is possible | off | +| `CC_PGPOOL_DISABLE_LOAD_BALANCE_ON_WRITE` | Load balance behavior when write query is issued in an explicit transaction. Valid values are "transaction", "trans_transaction", "dml_adaptive" or "always" | transaction | +| `CC_PGPOOL_DML_ADAPTIVE_OBJECT_RELATIONSHIP_LIST` | Comma separated list of object pairs | ' ' | +| `CC_PGPOOL_STATEMENT_LEVEL_LOAD_BALANCE` | Enables statement level load balancing | off | +| `CC_PGPOOL_SR_CHECK_PERIOD` | Streaming replication check period | 10 | +| `CC_PGPOOL_DELAY_THRESHOLD` | Threshold before not dispatching query to standby node Unit is in bytes | 10000000 | +| `CC_PGPOOL_HEALTH_CHECK_PERIOD` | Health check period | 20 | +| `CC_PGPOOL_HEALTH_CHECK_TIMEOUT` | Health check timeout | 20 | +| `CC_PGPOOL_HEALTH_CHECK_MAX_RETRIES` | Maximum number of times to retry a failed health check before giving up | 0 | +| `CC_PGPOOL_HEALTH_CHECK_RETRY_DELAY` | Amount of time to wait (in seconds) between retries | 1 | +| `CC_PGPOOL_CONNECT_TIMEOUT` | Timeout value in milliseconds before giving up to connect to backend | 10000 | +| `CC_PGPOOL_MEMORY_CACHE_ENABLED` | Use the memory cache functionality | off | +| `CC_PGPOOL_MEMQCACHE_TOTAL_SIZE` | Total memory size in megabytes for storing memory cache | 64 | +| `CC_PGPOOL_MEMQCACHE_MAX_NUM_CACHE` | Total number of cache entries | 1000000 | +| `CC_PGPOOL_MEMQCACHE_EXPIRE` | Memory cache entry life time specified in seconds | 0 | +| `CC_PGPOOL_MEMQCACHE_AUTO_CACHE_INVALIDATION` | Invalidation of query cache is triggered by corresponding DDL/DML/DCL | on | +| `CC_PGPOOL_MEMQCACHE_MAXCACHE` | Maximum SELECT result size in kilobytes (must be smaller than MEMQCACHE_CACHE_BLOCK_SIZE) | 400 | +| `CC_PGPOOL_MEMQCACHE_CACHE_BLOCK_SIZE` | Cache block size in megabytes | 1 | +| `CC_PGPOOL_CACHE_SAFE_MEMQCACHE_TABLE_LIST` | Comma separated list of table names to memcache that don't write to database (regexp are accepted) | ' ' | +| `CC_PGPOOL_CACHE_UNSAFE_MEMQCACHE_TABLE_LIST` | Comma separated list of table names not to memcache that don't write to database (regexp are accepted) | ' ' | ### Elastic Stack [Elastic Stack Documentation](/doc/addons/elastic) -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|`ELASTIC_APM_SERVER_URL` | URI to connect APM Server | Generated upon creation | ✓ | -|`ES_ADDON_APM_HOST` | APM Server hostname | Generated upon creation | ✓ | -|`ES_ADDON_APM_AUTH_TOKEN` | Authentication token to send metrics to APM Server | Generated upon creation | ✓ | -|`ELASTIC_APM_SECRET_TOKEN` | Authentication token to send metrics to APM Server | Generated upon creation | ✓ | -|`ES_ADDON_APM_USER` | Username credential used by APM Server to send metrics to Elasticsearch | Generated upon creation | ✓ | -|`ES_ADDON_APM_PASSWORD` | Password credential used by APM Server to send metrics to Elasticsearch | Generated upon creation | ✓ | -|`ES_ADDON_KIBANA_HOST` | Kibana hostname | Generated upon creation | ✓ | -|`ES_ADDON_KIBANA_USER` | Username credential used by Kibana to query Elasticsearch | Generated upon creation | ✓ | -|`ES_ADDON_KIBANA_PASSWORD` | Password credential used by Kibana to query Elasticsearch | Generated upon creation | ✓ | -|`ES_ADDON_URI` | URI to query Elasticsearch | Generated upon creation | ✓ | -|`ES_ADDON_HOST` | Elasticsearch hostname | Generated upon creation | ✓ | -|`ES_ADDON_USER` | Username credential to authenticate to Elasticsearch | Generated upon creation | ✓ | -|`ES_ADDON_PASSWORD` | Password credential to authenticate to Elasticsearch | Generated upon creation | ✓ | -|`ES_ADDON_VERSION` | ElasticSearch Version | 7 | ✓ | +| Name | Description | Default value | Read Only | +| -------------------------- | ----------------------------------------------------------------------- | ----------------------- | --------- | +| `ELASTIC_APM_SERVER_URL` | URI to connect APM Server | Generated upon creation | ✓ | +| `ES_ADDON_APM_HOST` | APM Server hostname | Generated upon creation | ✓ | +| `ES_ADDON_APM_AUTH_TOKEN` | Authentication token to send metrics to APM Server | Generated upon creation | ✓ | +| `ELASTIC_APM_SECRET_TOKEN` | Authentication token to send metrics to APM Server | Generated upon creation | ✓ | +| `ES_ADDON_APM_USER` | Username credential used by APM Server to send metrics to Elasticsearch | Generated upon creation | ✓ | +| `ES_ADDON_APM_PASSWORD` | Password credential used by APM Server to send metrics to Elasticsearch | Generated upon creation | ✓ | +| `ES_ADDON_KIBANA_HOST` | Kibana hostname | Generated upon creation | ✓ | +| `ES_ADDON_KIBANA_USER` | Username credential used by Kibana to query Elasticsearch | Generated upon creation | ✓ | +| `ES_ADDON_KIBANA_PASSWORD` | Password credential used by Kibana to query Elasticsearch | Generated upon creation | ✓ | +| `ES_ADDON_URI` | URI to query Elasticsearch | Generated upon creation | ✓ | +| `ES_ADDON_HOST` | Elasticsearch hostname | Generated upon creation | ✓ | +| `ES_ADDON_USER` | Username credential to authenticate to Elasticsearch | Generated upon creation | ✓ | +| `ES_ADDON_PASSWORD` | Password credential to authenticate to Elasticsearch | Generated upon creation | ✓ | +| `ES_ADDON_VERSION` | ElasticSearch Version | 7 | ✓ | ### Blackfire -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|`ENABLE_BLACKFIRE` | Enable the Blackfire extension | false | | -|`CC_BLACKFIRE_SERVER_TOKEN` | The server token used to authenticate with Blackfire | ' ' | ✓ | -|`CC_BLACKFIRE_SERVER_ID` | The server id used to authenticate with Blackfire | ' ' | ✓ | -|`CC_BLACKFIRE_LOG_LEVEL` | Sets the verbosity of Agent’s log output | 1 | ✓ | -|`CC_BLACKFIRE_MEMORY_LIMIT` | Sets the maximum allowed RAM usage (megabytes) when ingesting traces | 500 | ✓ | -|`CC_BLACKFIRE_COLLECTOR` | Sets the URL of Blackfire’s data collector | | ✓ | -|`CC_BLACKFIRE_TIMEOUT` | Sets the Blackfire API connection timeout | 15 | ✓ | -|`CC_BLACKFIRE_STATSD` | Sets the statsd server to send agent’s statistics | ' ' | ✓ | -|`CC_BLACKFIRE_STATSD_PREFIX` | Sets the statsd prefix to use when sending data | blackfire | ✓ | +| Name | Description | Default value | Read Only | +| ---------------------------- | -------------------------------------------------------------------- | ---------------------- | --------- | +| `ENABLE_BLACKFIRE` | Enable the Blackfire extension | false | | +| `CC_BLACKFIRE_SERVER_TOKEN` | The server token used to authenticate with Blackfire | ' ' | ✓ | +| `CC_BLACKFIRE_SERVER_ID` | The server id used to authenticate with Blackfire | ' ' | ✓ | +| `CC_BLACKFIRE_LOG_LEVEL` | Sets the verbosity of Agent’s log output | 1 | ✓ | +| `CC_BLACKFIRE_MEMORY_LIMIT` | Sets the maximum allowed RAM usage (megabytes) when ingesting traces | 500 | ✓ | +| `CC_BLACKFIRE_COLLECTOR` | Sets the URL of Blackfire’s data collector | | ✓ | +| `CC_BLACKFIRE_TIMEOUT` | Sets the Blackfire API connection timeout | 15 | ✓ | +| `CC_BLACKFIRE_STATSD` | Sets the statsd server to send agent’s statistics | ' ' | ✓ | +| `CC_BLACKFIRE_STATSD_PREFIX` | Sets the statsd prefix to use when sending data | blackfire | ✓ | ### New Relic -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|[`NEW_RELIC_APP_NAME`](https://docs.newrelic.com/docs/apm/agents/java-agent/configuration/java-agent-configuration-config-file/#ev-NEW_RELIC_APP_NAME "New Relic documentation") | Contains the application name | | -|[`NEW_RELIC_LICENSE_KEY`](https://docs.newrelic.com/docs/apm/agents/java-agent/configuration/java-agent-configuration-config-file/#ev-NEW_RELIC_LICENSE_KEY "New Relic documentation" ) | Contains your New Relic account license | | -|[`CC_NEWRELIC_BROWSER_MONITORING_AUTO_INSTRUMENT`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-autorum "New Relic documentation" ) | | | -|[`CC_NEWRELIC_DISTRIBUTED_TRACING_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-distributed-enabled "New Relic documentation" ) | | | -|[`CC_NEWRELIC_ERROR_COLLECTOR_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-err-enabled "New Relic documentation" ) | | | -|[`CC_NEWRELIC_TRANSACTION_TRACER_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-enable "New Relic documentation" ) | | | -|[`CC_NEWRELIC_TRANSACTION_TRACER_RECORD_SQL`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-sql "New Relic documentation" ) | obfuscated | | +| Name | Description | Default value | Read Only | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | ------------- | --------- | +| [`NEW_RELIC_APP_NAME`](https://docs.newrelic.com/docs/apm/agents/java-agent/configuration/java-agent-configuration-config-file/#ev-NEW_RELIC_APP_NAME "New Relic documentation") | Contains the application name | | | +| [`NEW_RELIC_LICENSE_KEY`](https://docs.newrelic.com/docs/apm/agents/java-agent/configuration/java-agent-configuration-config-file/#ev-NEW_RELIC_LICENSE_KEY "New Relic documentation" ) | Contains your New Relic account license | | | +| [`CC_NEWRELIC_BROWSER_MONITORING_AUTO_INSTRUMENT`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-autorum "New Relic documentation" ) | | | | +| [`CC_NEWRELIC_DISTRIBUTED_TRACING_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-distributed-enabled "New Relic documentation" ) | | | | +| [`CC_NEWRELIC_ERROR_COLLECTOR_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-err-enabled "New Relic documentation" ) | | | | +| [`CC_NEWRELIC_TRANSACTION_TRACER_ENABLED`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-enable "New Relic documentation" ) | | | | +| [`CC_NEWRELIC_TRANSACTION_TRACER_RECORD_SQL`](https://docs.newrelic.com/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-tt-sql "New Relic documentation" ) | obfuscated | | | ### Pulsar -| Name | Description | Default value | Read Only | -| ------------------------ | ----------------------------------------- | ------------------------------ | -------------------------- | -|`ADDON_PULSAR_BINARY_URL` | The complete URL to use in your application | Generated upon creation | ✓ | -|`ADDON_PULSAR_BINARY_PORT` | The port to connect to the Pulsar Cluster | Generated upon creation | ✓ | -|`ADDON_PULSAR_HOSTNAME` | The host to connect to the Pulsar Cluster | Generated upon creation | ✓ | -|`ADDON_PULSAR_HTTP_URL` | The complete URL to connect with WebSocket | Generated upon creation | ✓ | -|`ADDON_PULSAR_HTTP_PORT` | The port to connect with WebSocket | Generated upon creation | ✓ | -|`ADDON_PULSAR_NAMESPACE` | Your add-on Pulsar ID | Generated upon creation | ✓ | -|`ADDON_PULSAR_TENANT` | Your Clever Cloud tenant ID | Generated upon creation | ✓ | -|`ADDON_PULSAR_TOKEN` | Your Biscuit authentication token | Generated upon creation | ✓ | +| Name | Description | Default value | Read Only | +| -------------------------- | ------------------------------------------- | ----------------------- | --------- | +| `ADDON_PULSAR_BINARY_URL` | The complete URL to use in your application | Generated upon creation | ✓ | +| `ADDON_PULSAR_BINARY_PORT` | The port to connect to the Pulsar Cluster | Generated upon creation | ✓ | +| `ADDON_PULSAR_HOSTNAME` | The host to connect to the Pulsar Cluster | Generated upon creation | ✓ | +| `ADDON_PULSAR_HTTP_URL` | The complete URL to connect with WebSocket | Generated upon creation | ✓ | +| `ADDON_PULSAR_HTTP_PORT` | The port to connect with WebSocket | Generated upon creation | ✓ | +| `ADDON_PULSAR_NAMESPACE` | Your add-on Pulsar ID | Generated upon creation | ✓ | +| `ADDON_PULSAR_TENANT` | Your Clever Cloud tenant ID | Generated upon creation | ✓ | +| `ADDON_PULSAR_TOKEN` | Your Biscuit authentication token | Generated upon creation | ✓ | ### Redis [Redis Documentation](/doc/addons/redis) -| Name | Description | Default value | Read Only | -|-----------------------|------------------------------|--------------------------------|--------------------------------| -|`REDIS_HOST` | | Generated upon creation | ✓ | -|`REDIS_PORT` | | Generated upon creation | ✓ | -|`REDIS_PASSWORD` | | Generated upon creation | ✓ | +| Name | Description | Default value | Read Only | +| ---------------- | ----------- | ----------------------- | --------- | +| `REDIS_HOST` | | Generated upon creation | ✓ | +| `REDIS_PORT` | | Generated upon creation | ✓ | +| `REDIS_PASSWORD` | | Generated upon creation | ✓ | ### VPN @@ -619,15 +619,15 @@ to be routed through the exit node. > [!NOTE] > New setups use the `CC_VPN_*` names listed below. The previous `VPN_ADDON_*` names (and `VPN_TARGETS`) are deprecated and kept only for backward compatibility with existing setups. -| Name | Description | Default value | Read Only | -| --------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------ | -------------------------- | -| `CC_VPN_DNS_OVERRIDES` | Comma-separated list of DNS IP | | | -| `CC_VPN_CACRT` | Server CA certificate | | | -| `CC_VPN_CIPHER`| Cipher to use CIPHER, can be either {cipher_suite}:{hmac_alg} or only {cipher_suite} | DES-EDE3-CBC:SHA1 | | -| `CC_VPN_CRT` | Client certificate | | | -| `CC_VPN_DEVTYPE` | Kernel virtual interface kind to use ("tap" or "tun") | tap | | -| `CC_VPN_HOST` | Server host or IP address | | | -| `CC_VPN_KEY` | Client certificate private key | | | -| `CC_VPN_PORT` | Server port | | | -| `CC_VPN_TAKEY` | Pre-shared secret | | | -| `CC_VPN_TARGETS` | Comma-separated list of CIDRs for which you want the traffic to be routed through the exit node | | | +| Name | Description | Default value | Read Only | +| ---------------------- | ----------------------------------------------------------------------------------------------- | ----------------- | --------- | +| `CC_VPN_DNS_OVERRIDES` | Comma-separated list of DNS IP | | | +| `CC_VPN_CACRT` | Server CA certificate | | | +| `CC_VPN_CIPHER` | Cipher to use CIPHER, can be either {cipher_suite}:{hmac_alg} or only {cipher_suite} | DES-EDE3-CBC:SHA1 | | +| `CC_VPN_CRT` | Client certificate | | | +| `CC_VPN_DEVTYPE` | Kernel virtual interface kind to use ("tap" or "tun") | tap | | +| `CC_VPN_HOST` | Server host or IP address | | | +| `CC_VPN_KEY` | Client certificate private key | | | +| `CC_VPN_PORT` | Server port | | | +| `CC_VPN_TAKEY` | Pre-shared secret | | | +| `CC_VPN_TARGETS` | Comma-separated list of CIDRs for which you want the traffic to be routed through the exit node | | | diff --git a/content/guides/astro.md b/content/guides/astro.md index 87e929579..7160654fb 100644 --- a/content/guides/astro.md +++ b/content/guides/astro.md @@ -22,13 +22,16 @@ aliases: ## Overview Clever Cloud supports deploying both [fully static and on-demand rendered](https://docs.astro.build/en/basics/rendering-modes/) Astro projects: + - The `static` output mode is ideal for most content-oriented website, for which you have no need for per-visitor server-side customization. Consider using a [Static runtime](/doc/applications/static/) when using this output mode, with the automatic site generation. -- The `server` or `hybrid` output modes: consider using a [Node.js runtime](/doc/applications/nodejs) with [Astro’s Node adapter](https://docs.astro.build/en/guides/integrations-guide/node/) +- The `server` or `hybrid` output modes: consider using a [Node.js runtime](/doc/applications/nodejs) with [Astro’s Node adapter](https://docs.astro.build/en/guides/integrations-guide/node/) If you need an example source code, get [Astrowind](https://github.com/onwidget/astrowind) (you'll need [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Node.js](https://nodejs.org/en/download)): + ```bash git clone https://github.com/onwidget/astrowind myStaticApp ``` + To deploy your Astro project to Clever Cloud, you need to **create a new application**. {{% steps %}} diff --git a/content/guides/docs.md b/content/guides/docs.md index bdf7cbb58..def109982 100644 --- a/content/guides/docs.md +++ b/content/guides/docs.md @@ -110,7 +110,7 @@ LOGIN_REDIRECT_URL="https://" LOGOUT_REDIRECT_URL="https:///*" ``` -#### Push your code +#### Push the backend code If you push using git, add the remote as `clever-backend`, for example. @@ -120,7 +120,7 @@ If you push using git, add the remote as `clever-backend`, for example. {{% steps %}} -#### Create a Node.js application +#### Create the frontend Node.js application Select at least a `M` instance for the build, and inject the following environment variables: @@ -139,7 +139,7 @@ NODE_OPTIONS="--max-old-space-size=4096" - Select **Domain names** and set the base domain for Docs. The frontend doesn't need any route. - Add the domain to the environment variables: inject `NEXT_PUBLIC_API_ORIGIN="https://"` to the list of the frontend environment variables. -#### Push your code +#### Push the frontend code If you push using git, add the remote as `clever-frontend`, for example. @@ -149,7 +149,7 @@ If you push using git, add the remote as `clever-frontend`, for example. {{% steps %}} -#### Create a Node.js application +#### Create the y-provider Node.js application Inject the following environment variables: @@ -187,7 +187,7 @@ COLLABORATION_SERVER_SECRET="" Then select the **backend** application > **Service dependencies** > **Link applications** and choose the y-provider application. -#### Push your code +#### Push the y-provider code If you push using git, add the remote as `clever-y-provider`, for example. @@ -245,7 +245,7 @@ Find it in **Clients > impress > credentials**, named **Client secret*. You can choose among different identity providers (GitHub, Google, etc, and even Clever Cloud). -#### Inject the variables in the **backend** application +#### Inject the Keycloak variables in the **backend** application ```env OIDC_OP_AUTHORIZATION_ENDPOINT="https:///realms/impress/protocol/openid-connect/auth" @@ -275,7 +275,7 @@ Docs uses s3 compatible storage to store uploaded files by users. #### Create a bucket -#### Inject the variables in the **backend** application +#### Inject the Cellar variables in the **backend** application ```env AWS_S3_ACCESS_KEY_ID="" @@ -292,5 +292,5 @@ AWS_RESPONSE_CHECKSUM_VALIDATION="when_required" ## 🎓 Further Help {{< cards >}} - {{< card link="https://github.com/suitenumerique/docs/blob/main/docs/installation/kubernetes.md#preparation" title="Docs documentation" subtitle="Installation instructions" icon="adjustments-horizontal" >}} + {{< card link="" title="Docs documentation" subtitle="Installation instructions" icon="adjustments-horizontal" >}} {{< /cards >}} diff --git a/content/guides/docusaurus.md b/content/guides/docusaurus.md index 5b7f00be2..fe9c756d3 100644 --- a/content/guides/docusaurus.md +++ b/content/guides/docusaurus.md @@ -45,5 +45,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="https://docusaurus.io/docs" title="Learn Docusaurus" subtitle="How to write and organize your content" icon="docusaurus" >}} + {{< card link="" title="Learn Docusaurus" subtitle="How to write and organize your content" icon="docusaurus" >}} {{< /cards >}} diff --git a/content/guides/ekg-statsd-haskell-metrics.md b/content/guides/ekg-statsd-haskell-metrics.md index 476207a05..17b488e0d 100644 --- a/content/guides/ekg-statsd-haskell-metrics.md +++ b/content/guides/ekg-statsd-haskell-metrics.md @@ -26,10 +26,10 @@ If you're using [warp](https://hackage.haskell.org/package/warp), you can use `w You need the following packages in your [dependencies](/doc/applications/haskell#dependencies): -* ekg-core -* ekg-statsd -* scotty -* wai-middleware-metrics +- ekg-core +- ekg-statsd +- scotty +- wai-middleware-metrics Then add this Hello World code to your application and modify it to fit your needs: diff --git a/content/guides/eleventy.md b/content/guides/eleventy.md index 38e874910..ab4bded01 100644 --- a/content/guides/eleventy.md +++ b/content/guides/eleventy.md @@ -47,5 +47,5 @@ clever env set CC_BUILD_COMMAND "npx @11ty/eleventy" {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="https://www.11ty.dev/docs/" title="Learn Eleventy (11ty)" subtitle="How to write and organize your content" icon="11ty" >}} + {{< card link="" title="Learn Eleventy (11ty)" subtitle="How to write and organize your content" icon="11ty" >}} {{< /cards >}} diff --git a/content/guides/fluentd.md b/content/guides/fluentd.md index b846365df..371f1844c 100644 --- a/content/guides/fluentd.md +++ b/content/guides/fluentd.md @@ -19,11 +19,11 @@ aliases: Since you deploy microservices on Clever Cloud, you may need some data pipes between your services to: -* collect data from your PostgreSQL to create Elasticsearch indexes for your website search engine -* collect application logs to analyze them with Elasticsearch, then archive them with S3 -* collect Apache access logs to analyze them in MongoDB -* extract data from the database of your PHP/MySQL application to transform then load them in your other node.js/PostgreSQL application -* and many more … +- collect data from your PostgreSQL to create Elasticsearch indexes for your website search engine +- collect application logs to analyze them with Elasticsearch, then archive them with S3 +- collect Apache access logs to analyze them in MongoDB +- extract data from the database of your PHP/MySQL application to transform then load them in your other node.js/PostgreSQL application +- and many more … Fluentd is an open source data collector written in Ruby, which lets you unify the data collection and consumption for a better use and understanding of data. @@ -41,12 +41,12 @@ Fluentd is an open source data collector written in Ruby, which lets you unify t To follow this tutorial, you will need: -* Ruby >= 2.4.4 (w/ Rubygems) -* Bundler -* Docker -* Git -* curl -* a Ruby versions manager +- Ruby >= 2.4.4 (w/ Rubygems) +- Bundler +- Docker +- Git +- curl +- a Ruby versions manager {{< callout type="info" >}} To manage your gems and ruby versions, we recommend [rbenv](https://github.com/sstephenson/rbenv). diff --git a/content/guides/ghost.md b/content/guides/ghost.md index 629f8ac8f..9220aa68d 100644 --- a/content/guides/ghost.md +++ b/content/guides/ghost.md @@ -273,7 +273,7 @@ The rebuild downloads the selected release and installs dependencies for the con ## Learn more {{< cards >}} - {{< card link="https://docs.ghost.org/" title="Ghost documentation" subtitle="Configure and manage a Ghost publication" icon="ghost" >}} + {{< card link="" title="Ghost documentation" subtitle="Configure and manage a Ghost publication" icon="ghost" >}} {{< card link="/developers/doc/applications/linux" title="Linux application runtime" subtitle="Configure and deploy any application" icon="linux" >}} {{< card link="/developers/doc/addons/mysql" title="MySQL" subtitle="Explore managed MySQL databases" icon="mysql" >}} {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="Explore S3-compatible object storage" icon="cellar" >}} diff --git a/content/guides/go-echoip.md b/content/guides/go-echoip.md index 18dc402ed..3acf76c65 100644 --- a/content/guides/go-echoip.md +++ b/content/guides/go-echoip.md @@ -19,14 +19,13 @@ This doc explains how to install and configure EchoIP from source, and how to de ## How to Configure and Deploy EchoIP on Clever Cloud - ### Download EchoIP You can download EchoIP from and create a new origin. - First clone EchoIP's repository: -``` + +```bash ~ $ git clone https://github.com/mpolden/echoip.git ~ $ cd echoip ``` @@ -34,7 +33,8 @@ First clone EchoIP's repository: ### Configure `clevercloud/go.json` Create the necessary files to build and run the application: -``` + +```text echoip/ ~ $ mkdir clevercloud echoip/ ~ $ cat << EOF > clevercloud/go.json { @@ -48,33 +48,31 @@ echoip/ ~ $ git add clevercloud/ echoip/ ~ $ git commit -m "add clevercloud files" clevercloud/ ``` - {{% content "set-env-vars" %}} - - Define necessary environment variables (this is specific to EchoIP): -``` + +```text echoip/ ~ $ clever env set CC_RUN_COMMAND "~/go_home/bin/echoip -H X-Forwarded-For" Your environment variable has been successfully saved ``` - {{% content "deploy-git" %}} - - Add the clevercloud git remote: -``` + +```text echoip/ ~ $ git remote add clevercloud $(jq -r '.apps[0].git_ssh_url' < .clever.json) ``` Push the app for deployment: -``` + +```text echoip/ ~ $ git push clevercloud master ``` Check the deployment logs: -``` + +```text echoip/ ~ $ clever logs ``` diff --git a/content/guides/hexo.md b/content/guides/hexo.md index 733f7df51..358fee64c 100644 --- a/content/guides/hexo.md +++ b/content/guides/hexo.md @@ -18,6 +18,7 @@ keywords: ## Requirements If you need an example source code, initialize an Hexo project with [Cactus theme](https://github.com/probberechts/hexo-theme-cactus) (you'll need [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Node.js](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs)): + ```bash npx hexo init myStaticApp --no-install ``` @@ -51,5 +52,5 @@ clever env set CC_BUILD_COMMAND "npm run build" {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="https://hexo.io/docs/" title="Learn Hexo" subtitle="How to write and organize your content" icon="hexo" >}} + {{< card link="" title="Learn Hexo" subtitle="How to write and organize your content" icon="hexo" >}} {{< /cards >}} diff --git a/content/guides/hono.md b/content/guides/hono.md index cbb46bb2c..6b4662123 100644 --- a/content/guides/hono.md +++ b/content/guides/hono.md @@ -32,7 +32,7 @@ To deploy your Hono project to Clever Cloud, adapt its entry point, then create ### Adapt the entry point -The `nodejs` template's `src/index.ts` starts a server on port 3000 and has no shutdown handling. Adapt both behaviours before deploying. +The `nodejs` template's `src/index.ts` starts a server on port 3000 and has no shutdown handling. Adapt both behaviours before deploying. Clever Cloud sets `PORT` environment variable to `8080` by default and expects the application to listen on every network interface. Read it rather than hardcoding this value to remain compatible with [Request Flow port allocation](/developers/doc/develop/request-flow/#port-management), keep port 3000 as a local fallback, and set the hostname to `0.0.0.0`. Also close the server on `SIGINT` and `SIGTERM` so in-flight requests can finish during a restart or zero-downtime deployment: @@ -125,5 +125,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/nodejs" title="Deploy a Node.js application" subtitle="Learn more on deploying a Node.js application" icon="node" >}} - {{< card link="https://hono.dev/docs" title="Hono documentation" subtitle="Learn more about the Hono framework" icon="hono" >}} + {{< card link="" title="Hono documentation" subtitle="Learn more about the Hono framework" icon="hono" >}} {{< /cards >}} diff --git a/content/guides/hugo-static-s3.md b/content/guides/hugo-static-s3.md index 9704809f8..772cd5543 100644 --- a/content/guides/hugo-static-s3.md +++ b/content/guides/hugo-static-s3.md @@ -52,7 +52,7 @@ To make your bucket publicly accessible, you have to apply a policy to the bucke To set your bucket as public, you have to apply the following policy which you can save in a file named `policy.json`: -```json{filename="policy.json"} +```json {filename="policy.json"} { "Id": "Policy1587216857769", "Version": "2012-10-17", diff --git a/content/guides/hugo.md b/content/guides/hugo.md index ec655a365..36f1115a9 100644 --- a/content/guides/hugo.md +++ b/content/guides/hugo.md @@ -54,5 +54,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="https://gohugo.io/documentation/" title="Learn Hugo" subtitle="How to write and organize your content" icon="hugo" >}} + {{< card link="" title="Learn Hugo" subtitle="How to write and organize your content" icon="hugo" >}} {{< /cards >}} diff --git a/content/guides/kibana.md b/content/guides/kibana.md index 6368fdacc..5d4f0fcbd 100644 --- a/content/guides/kibana.md +++ b/content/guides/kibana.md @@ -32,7 +32,7 @@ Kibana can be enabled at the add-on creation. Choose "Create an add-on" > "Elast You can customize the Kibana configuration file by setting the `CC_PRE_RUN_HOOK` environment variable. This variable will be executed before the Kibana instance starts. By default, it is: -``` +```text CC_PRE_RUN_HOOK` = `curl https://api.clever-cloud.com/v2/providers/es-addon/kibana-setup/ | sh` ``` @@ -47,7 +47,7 @@ Check other available configuration file on [GitHub](https://github.com/CleverCl To disable SSO authentication and use elastic users instead, you need to modify Kibana's configuration file. We provide an example configuration file for Kibana 8.3.3: -* https://raw.githubusercontent.com/CleverCloud/custom-kibana-config/master/8.3.3/no-sso-8.3.3 +- Remember, you need to ask Clever Cloud Support team to grant superuser permissions to your user. After that, you will be able to add additional users via Kibana. @@ -55,7 +55,7 @@ Remember, you need to ask Clever Cloud Support team to grant superuser permissio If you need to allow for multiple authentication methods to your Kibana, you need to modify Kibana's configuration. We provide an example configuration file for Kibana 8.10.2: -* https://raw.githubusercontent.com/CleverCloud/custom-kibana-config/master/8.10.2/sso-basic-8.10.2 +- ### Add custom domain name @@ -69,9 +69,9 @@ Follow these steps : 1. Download/unzip Kibana in version which match with your Elastic Stack version 2. Edit kibana.yml : - * line 43 : `elasticsearch.host: :443` - * line 49 : `elasticsearch.username: kibana` - * line 50 : `elasticsearch.password: ` - * Launch Kibana (`/bin/kibana`) + - line 43 : `elasticsearch.host: :443` + - line 49 : `elasticsearch.username: kibana` + - line 50 : `elasticsearch.password: ` + - Launch Kibana (`/bin/kibana`) 3. Go to Kibana through the dedicated local address: `http://localhost:5601` 4. Connect to ElasticSearch with your Elastic username and password diff --git a/content/guides/lume-deno.md b/content/guides/lume-deno.md index 78bd57ad9..a816b4aa9 100644 --- a/content/guides/lume-deno.md +++ b/content/guides/lume-deno.md @@ -22,6 +22,7 @@ If you need an example source code, use the [Lume website](https://github.com/lu ```bash git clone https://github.com/lumeland/lume.land myStaticApp ``` + {{% content "static-create" %}} ### Environment variables @@ -58,5 +59,5 @@ It uses [Mise package manager](/doc/reference/reference-environment-variables/#i {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="https://lume.land/docs/overview/about-lume/" title="Learn Lume" subtitle="How to write and organize your content" icon="deno" >}} + {{< card link="" title="Learn Lume" subtitle="How to write and organize your content" icon="deno" >}} {{< /cards >}} diff --git a/content/guides/mdbook.md b/content/guides/mdbook.md index 16d4cd569..54b7eebd8 100644 --- a/content/guides/mdbook.md +++ b/content/guides/mdbook.md @@ -43,5 +43,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="https://rust-lang.github.io/mdBook/" title="Learn mdBook" subtitle="How to write and organize your content" icon="mdbook" >}} + {{< card link="" title="Learn mdBook" subtitle="How to write and organize your content" icon="mdbook" >}} {{< /cards >}} diff --git a/content/guides/mkdocs.md b/content/guides/mkdocs.md index f1a82d3fb..c7683aedb 100644 --- a/content/guides/mkdocs.md +++ b/content/guides/mkdocs.md @@ -24,6 +24,7 @@ pipx mkdocs new myStaticApp ``` You can also use [uvx](https://docs.astral.sh/uv/guides/tools/): + ```bash uvx mkdocs new myStaticApp ``` @@ -47,5 +48,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Static runtime documentation" subtitle="How to configure your website" icon="static" >}} - {{< card link="https://www.mkdocs.org/getting-started/" title="MkDocs documentation" subtitle="How to write and organize your content" icon="docs" >}} + {{< card link="" title="MkDocs documentation" subtitle="How to write and organize your content" icon="docs" >}} {{< /cards >}} diff --git a/content/guides/moodle.md b/content/guides/moodle.md index 9313c46d2..845d95cce 100644 --- a/content/guides/moodle.md +++ b/content/guides/moodle.md @@ -136,7 +136,6 @@ Create a `clevercloud/cron.json` file with a string to run `cron.sh`every minute You might encounter errors when the Cron tries to access `moodledata` in your FS Bucket. For FS Bucket backups, look for a dedicated tool like [rclone](https://rclone.org). - ## 🎓 Further Help {{< cards >}} @@ -144,7 +143,7 @@ You might encounter errors when the Cron tries to access `moodledata` in your FS {{< card link="/developers/doc/administrate/cron" title="CRON" subtitle="Set up a CRON job for your app" icon="code-bracket" >}} {{< card link="/developers/doc/addons/fs-bucket" title="FS Bucket" subtitle="External File System for your apps" icon="fsbucket" >}} {{< card link="/developers/doc/addons/mysql" title="MySQL" icon="mysql" subtitle="Your self-hosted managed relational database" >}} - {{< card link="https://docs.moodle.org/en/Installation_quick_guide" title="Moodle Documentation" subtitle="Check Moodle installation guide" icon="moodle" >}} + {{< card link="" title="Moodle Documentation" subtitle="Check Moodle installation guide" icon="moodle" >}} {{< /cards >}} See [Moodle installation documentation](https://docs.moodle.org/en/Installation_quick_guide) for further help and development configuration. diff --git a/content/guides/node-js-mongo-db.md b/content/guides/node-js-mongo-db.md index a4d39910b..617141307 100644 --- a/content/guides/node-js-mongo-db.md +++ b/content/guides/node-js-mongo-db.md @@ -19,7 +19,7 @@ aliases: The goal of this article is to show you how to deploy a Node.js + MongoDB application on Clever Cloud. The application is a very simple todo list. You can add and delete values. More information about the application: -* [GitHub repo](https://github.com/CleverCloud/demo-nodejs-mongodb-rest) +- [GitHub repo](https://github.com/CleverCloud/demo-nodejs-mongodb-rest) {{% content "create-application" %}} diff --git a/content/guides/nuxt.md b/content/guides/nuxt.md index 21d796ccc..901797471 100644 --- a/content/guides/nuxt.md +++ b/content/guides/nuxt.md @@ -33,7 +33,7 @@ git clone https://github.com/renegadevi/nuxt-boilerplate myStaticApp Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. -### Automatic build +## Automatic build The Static runtime automatically detects `nuxt.config.ts`, runs the Nuxt static generation command and serves the generated files. @@ -95,5 +95,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/nodejs" title="Deploy a Node.js application" subtitle="Learn more on deploying a Node.js application" icon="node" >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="Learn more on deploying a Static application" icon="static" >}} - {{< card link="https://nuxt.com/docs/4.x/getting-started/introduction" title="Learn Nuxt" subtitle="How to write and organize your content" icon="nuxt" >}} + {{< card link="" title="Learn Nuxt" subtitle="How to write and organize your content" icon="nuxt" >}} {{< /cards >}} diff --git a/content/guides/outline.md b/content/guides/outline.md index 4e17dd356..430676072 100644 --- a/content/guides/outline.md +++ b/content/guides/outline.md @@ -205,14 +205,14 @@ clever scale --build-flavor M Outline needs at least one authentication method before users can sign in and supports the following methods: -| Authentication method | Required variables | Callback URL | -| --- | --- | --- | -| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET` | `$URL/auth/discord.callback` | -| Email magic links | `SMTP_HOST` or `SMTP_SERVICE`, `SMTP_FROM_EMAIL` | — | -| Google | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | `$URL/auth/google.callback` | -| Microsoft Entra ID | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET` | `$URL/auth/azure.callback` | -| OpenID Connect | `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, `OIDC_ISSUER_URL` | `$URL/auth/oidc.callback` | -| Slack | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET` | `$URL/auth/slack.callback` | +| Authentication method | Required variables | Callback URL | +| --------------------- | --------------------------------------------------------- | ---------------------------- | +| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET` | `$URL/auth/discord.callback` | +| Email magic links | `SMTP_HOST` or `SMTP_SERVICE`, `SMTP_FROM_EMAIL` | — | +| Google | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | `$URL/auth/google.callback` | +| Microsoft Entra ID | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET` | `$URL/auth/azure.callback` | +| OpenID Connect | `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, `OIDC_ISSUER_URL` | `$URL/auth/oidc.callback` | +| Slack | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET` | `$URL/auth/slack.callback` | Email magic links require a complete [SMTP configuration](https://docs.getoutline.com/s/hosting/doc/smtp-cqCJyZGMIB), including the credentials and connection settings required by your email provider. Configure the matching callback URL in your provider, then set its variables. For OpenID Connect, also allow the application URL (`$URL`) as a post-logout redirect URI if the provider exposes a logout endpoint. For example: @@ -267,5 +267,5 @@ Replace the example values with the versions you selected. Outline applies pendi {{< card link="/developers/doc/addons/postgresql" title="PostgreSQL" subtitle="Explore managed PostgreSQL databases" icon="pg" >}} {{< card link="/developers/doc/addons/redis" title="Redis" subtitle="Explore managed Redis databases" icon="redis" >}} {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="Explore S3-compatible object storage" icon="fsbucket" >}} - {{< card link="https://docs.getoutline.com/s/hosting" title="Outline hosting documentation" subtitle="Learn more about self-hosting Outline" icon="outline" >}} + {{< card link="" title="Outline hosting documentation" subtitle="Learn more about self-hosting Outline" icon="outline" >}} {{< /cards >}} diff --git a/content/guides/pgpool.md b/content/guides/pgpool.md index cde7f3642..87ca4bde7 100644 --- a/content/guides/pgpool.md +++ b/content/guides/pgpool.md @@ -111,7 +111,7 @@ For the `HOST` and `PORT`, you must use the values of the `POSTGRESQL_ADDON_DIRE An example of the `CC_PGPOOL_FOLLOWERS` variable with two followers: -```json{linenos=table} +```json {linenos=table} [ { "hostname": "", diff --git a/content/guides/ruby-rack-app-tutorial.md b/content/guides/ruby-rack-app-tutorial.md index 83a769a37..dacc41f75 100644 --- a/content/guides/ruby-rack-app-tutorial.md +++ b/content/guides/ruby-rack-app-tutorial.md @@ -24,10 +24,10 @@ Created in 2007, Rack has become the de-facto standard for ruby web applications To follow this tutorial, you will need: -* Ruby >= 1.9.2 (w/ Rubygems) -* Bundler (`gem install bundler` and you're good to go!) -* Your preferred editor -* Git (for the deploy part) +- Ruby >= 1.9.2 (w/ Rubygems) +- Bundler (`gem install bundler` and you're good to go!) +- Your preferred editor +- Git (for the deploy part) {{< callout type="info">}} To manage your gems and ruby versions, we recommend [rbenv](https://github.com/sstephenson/rbenv). If you use a system-wide installation of ruby, You will have to use `sudo` with the `gem` and `bundle` commands, or use arguments that will make gem and bundle install the gem in directories you have write-permissions in. diff --git a/content/guides/ruby-rack.md b/content/guides/ruby-rack.md index 4323d5493..2a507035c 100644 --- a/content/guides/ruby-rack.md +++ b/content/guides/ruby-rack.md @@ -29,10 +29,10 @@ Created in 2007, Rack has become the de-facto standard for ruby web applications Be sure that: -* Bundler is installed locally (`gem install bundler`) -* you have a `config.ru` file -* Git (for the deploy part) -* you have a `gems.rb` or `Gemfile` containing your dependencies +- Bundler is installed locally (`gem install bundler`) +- you have a `config.ru` file +- Git (for the deploy part) +- you have a `gems.rb` or `Gemfile` containing your dependencies ### Tutorial and sample app diff --git a/content/guides/sftpgo.md b/content/guides/sftpgo.md index e7789347e..f9f0082d8 100644 --- a/content/guides/sftpgo.md +++ b/content/guides/sftpgo.md @@ -164,15 +164,15 @@ A new Cellar add-on does not contain a bucket. Create one with Cellar Explorer i Create a user in the SFTPGo web administration interface and select **S3-compatible** as its storage provider: -| Setting | Value | -| ------- | ----- | -| Bucket | Your Cellar bucket name | -| Home Dir | `/tmp/sftpgo` | -| Region | `default` | -| Access Key | The `CELLAR_ADDON_KEY_ID` value | -| Access Secret | The `CELLAR_ADDON_KEY_SECRET` value | -| Endpoint | `https://` followed by the `CELLAR_ADDON_HOST` value | -| Use path-style addressing | Enabled | +| Setting | Value | +| ------------------------- | ---------------------------------------------------- | +| Bucket | Your Cellar bucket name | +| Home Dir | `/tmp/sftpgo` | +| Region | `default` | +| Access Key | The `CELLAR_ADDON_KEY_ID` value | +| Access Secret | The `CELLAR_ADDON_KEY_SECRET` value | +| Endpoint | `https://` followed by the `CELLAR_ADDON_HOST` value | +| Use path-style addressing | Enabled | SFTPGo requires a local home directory for temporary files when using S3-compatible storage. The startup task creates `/tmp/sftpgo` on every application instance. The open-source edition [uses unlinked local files for transfers to and from object storage](https://github.com/drakkan/sftpgo/discussions/1968), so a transfer can temporarily consume its full size on the instance disk. Account for the largest expected files and simultaneous transfers when choosing the run instance; completed files remain stored in Cellar, but in-progress transfers can fail if local disk is exhausted. @@ -271,5 +271,5 @@ Use any SFTP client with the following settings: {{< card link="/developers/doc/applications/linux" title="Linux applications" subtitle="Deploy any application" icon="linux" >}} {{< card link="/developers/doc/addons/cellar" title="Cellar object storage" subtitle="Store files with an S3 API" icon="database" >}} {{< card link="/developers/doc/addons/fs-bucket" title="FS Buckets" subtitle="Store files with SFTP access" icon="fsbucket" >}} - {{< card link="https://docs.sftpgo.com/latest/" title="SFTPGo documentation" subtitle="Configure users and storage" icon="book-open" >}} + {{< card link="" title="SFTPGo documentation" subtitle="Configure users and storage" icon="book-open" >}} {{< /cards >}} diff --git a/content/guides/sveltekit.md b/content/guides/sveltekit.md index 54e28dd2b..f8d57b06f 100644 --- a/content/guides/sveltekit.md +++ b/content/guides/sveltekit.md @@ -228,5 +228,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/nodejs/" title="Node.js applications" subtitle="Configure and deploy Node.js applications" icon="node" >}} {{< card link="/developers/doc/applications/static/" title="Static applications" subtitle="Configure and deploy static applications" icon="static" >}} - {{< card link="https://svelte.dev/docs/kit/adapters" title="SvelteKit adapters" subtitle="Choose and configure a SvelteKit adapter" icon="svelte" >}} + {{< card link="" title="SvelteKit adapters" subtitle="Choose and configure a SvelteKit adapter" icon="svelte" >}} {{< /cards >}} diff --git a/content/guides/tutorial-drupal.md b/content/guides/tutorial-drupal.md index 520eb884b..a102a4054 100644 --- a/content/guides/tutorial-drupal.md +++ b/content/guides/tutorial-drupal.md @@ -39,9 +39,9 @@ Make sure you have created a MySQL database add-on in the Clever Cloud console, We at this point assume you have downloaded the source files of drupal from [drupal.org](https://new.drupal.org/download) and already have linked your MySQL add-on. -* Open `.gitignore` file and delete `sites/*/settings*.php` line -* Copy the file `sites/default/default.settings.php` to `sites/default/settings.php` -* Open `sites/default/settings.php` and line 213, replace +- Open `.gitignore` file and delete `sites/*/settings*.php` line +- Copy the file `sites/default/default.settings.php` to `sites/default/settings.php` +- Open `sites/default/settings.php` and line 213, replace ```php{linenos=table} $databases = array(); @@ -67,12 +67,12 @@ by ); ``` -* Replace the line `$settings['hash_salt'] = ''` (`$drupal_hash_salt` for Drupal 7) with `$settings['hash_salt'] = getenv('DRUPAL_SALT')`. You can generate salts with [this link](https://www.passwordtool.hu/). Add a new `DRUPAL_SALT` [environment variable](/doc/applications/php#configure-your-php-application) to the application with the salt you have generated. -* As mentioned in this [article](/doc/addons/fs-bucket), with Git deployments, files that are uploaded by users must be +- Replace the line `$settings['hash_salt'] = ''` (`$drupal_hash_salt` for Drupal 7) with `$settings['hash_salt'] = getenv('DRUPAL_SALT')`. You can generate salts with [this link](https://www.passwordtool.hu/). Add a new `DRUPAL_SALT` [environment variable](/doc/applications/php#configure-your-php-application) to the application with the salt you have generated. +- As mentioned in this [article](/doc/addons/fs-bucket), with Git deployments, files that are uploaded by users must be persisted in a File System Bucket. In order to do so, [add a File Bucket](/doc/addons/fs-bucket) via the console. -* At the root of your application, create a `clevercloud/buckets.json` file (create a `clevercloud` +- At the root of your application, create a `clevercloud/buckets.json` file (create a `clevercloud` folder in which you create a `buckets.json` file). -* Copy the `bucket.json` content from the FS bucket addon dashboard (make sure to edit the `folder` field): +- Copy the `bucket.json` content from the FS bucket addon dashboard (make sure to edit the `folder` field): ```javascript{linenos=table} [ @@ -83,8 +83,8 @@ folder in which you create a `buckets.json` file). ] ``` -* Send these Drupal files via Git. -* When finished, get the url that you can find in the *domains* panel in the left sidebar. Then open the following link: +- Send these Drupal files via Git. +- When finished, get the url that you can find in the *domains* panel in the left sidebar. Then open the following link: `https://yourapplication.cleverapps.io/install.php` diff --git a/content/guides/tutorial-laravel.md b/content/guides/tutorial-laravel.md index f2b3de230..b78f58781 100644 --- a/content/guides/tutorial-laravel.md +++ b/content/guides/tutorial-laravel.md @@ -176,5 +176,5 @@ This environment variable exists in any Clever Cloud instance. This configuratio {{< card link="/developers/doc/metrics/new-relic" title="Monitor your App with New Relic" subtitle="Connect your application to New Relic." icon="new-relic" >}} {{< card link="/developers/doc/addons/fs-bucket" title="FS Bucket" subtitle="External File System for your apps" icon="fsbucket" >}} {{< card link="/developers/doc/develop/build-hooks" title="Deployment hooks" subtitle="Learn about custom commands used in this guide." icon="rocket-launch" >}} - {{< card link="https://laravel.com/docs/10.x/deployment" title="Laravel Documentation" subtitle="Read Laravel documentation on deployments." icon="laravel" >}} + {{< card link="" title="Laravel Documentation" subtitle="Read Laravel documentation on deployments." icon="laravel" >}} {{< /cards >}} diff --git a/content/guides/tutorial-symfony.md b/content/guides/tutorial-symfony.md index b60d1404d..012316916 100644 --- a/content/guides/tutorial-symfony.md +++ b/content/guides/tutorial-symfony.md @@ -58,7 +58,7 @@ You can anyway add your environment with any of the methods mentioned in [Settin For your application logs to be collected and available in the console and CLI, you need to configure monolog to use its `error_log` output. That does not mean that it will only output error level logs, you can set it to use any level, here is an example with the info level (and above): -```yaml{filename="config_prod.yml"} +```yaml {filename="config_prod.yml"} monolog: handlers: filter_for_errors: @@ -82,7 +82,7 @@ You can use the `CC_REVERSE_PROXY_IPS` [environment variable](#setting-up-enviro TRUSTED_PROXIES=127.0.0.1,${CC_REVERSE_PROXY_IPS} ``` -```yaml{filename="config/packages/framework.yaml"} +```yaml {filename="config/packages/framework.yaml"} framework: # … trusted_proxies: '%env(TRUSTED_PROXIES)%' @@ -113,7 +113,7 @@ To manage your connection pool towards your MySQL add-on, you can set-up a [Prox Once you have activated the ProxySQL (through the environment variable), a configuration example would be: -```yaml{filename="doctrine.yaml"} +```yaml {filename="doctrine.yaml"} dbal: unix_socket: '%env(CC_MYSQL_PROXYSQL_SOCKET_PATH)%' url: 'mysql://%env(MYSQL_ADDON_USER)%:%env(MYSQL_ADDON_PASSWORD)%@localhost/%env(MYSQL_ADDON_DB)%?serverVersion=%env(MYSQL_ADDON_VERSION)%' diff --git a/content/guides/tutorial-wordpress.md b/content/guides/tutorial-wordpress.md index 44b5a328e..b89ed3bcc 100644 --- a/content/guides/tutorial-wordpress.md +++ b/content/guides/tutorial-wordpress.md @@ -51,7 +51,7 @@ define( 'DB_HOST', getenv("MYSQL_ADDON_HOST").':'.getenv("MYSQL_ADDON_PORT") ); ``` {{< callout type="warning" >}} -**Make sure you use ** in `wp-config.php` the port the add-on dashboard provides. Use the `MYSQL_ADDON_PORT` environment variable for that. We **don't** use the default MySQL port. +**Make sure you use** in `wp-config.php` the port the add-on dashboard provides. Use the `MYSQL_ADDON_PORT` environment variable for that. We **don't** use the default MySQL port. {{< /callout >}} ### SSL Configuration diff --git a/content/postmortem/2024-08-02.md b/content/postmortem/2024-08-02.md index 3a6e08826..a2b5a6bf2 100644 --- a/content/postmortem/2024-08-02.md +++ b/content/postmortem/2024-08-02.md @@ -14,8 +14,8 @@ On Friday, August 2nd, 2024 Clever Cloud’s platform became unstable, leading t These events occurred because: -* A planned minor maintenance of the Messaging component has locked the cluster causing an incident ([www.clevercloudstatus.com/incidents/880](https://www.clevercloudstatus.com/incidents/880)) between **2:00 PM CEST** and **2:29 PM CEST** -* As an indirect consequence, the infrastructure experienced I/O and memory pressure between **3:40 PM CEST** and **4:16 PM CEST** causing some hypervisors to crash. +- A planned minor maintenance of the Messaging component has locked the cluster causing an incident ([www.clevercloudstatus.com/incidents/880](https://www.clevercloudstatus.com/incidents/880)) between **2:00 PM CEST** and **2:29 PM CEST** +- As an indirect consequence, the infrastructure experienced I/O and memory pressure between **3:40 PM CEST** and **4:16 PM CEST** causing some hypervisors to crash. Impacts were observed for customers relying on the EU-FR-1 (PAR) region, and remote zones depending on the EU-FR-1 control plane (OVHcloud, Scaleway, and Oracle). Privates and on-premise zones weren’t impacted. @@ -593,17 +593,17 @@ Some of our hypervisors had processes stuck in `TASK_UNINTERRUPTIBLE` for too lo During the event, the monitoring infrastructure triggered a lot of events for both infrastructure and customer services. We observed almost x3 deployments as usual during that day. Multiple reasons can explain this observation: -* The loss of hypervisors due to kernel panics made a lot of instances unreachable, leading to lots of deployments to honor the contract (e.g. number of minimum instances) -* VMs that were under memory pressure were usually unresponsive and so they were (correctly) seen as unreachable -* VMs that were on overloaded hypervisors might have also been slowed down due to the high CPU usage of the hypervisor. Those VMs had less CPU time to do their usual operations, making their load average increase. Our monitoring took that as an increase of CPU usage and they were ordered to upscale if they were configured to -* Some of our monitoring instances took more time to do their checks because they were overloaded as well. Because the monitoring data was missing due to late checks, it was interpreted as lack of data and so as unreachable VMs, triggering more Monitoring/Unreachable events -* Customers seeing their service unavailable are trying to trigger redeployment of their applications, which adds more scheduling tasks on the infrastructure +- The loss of hypervisors due to kernel panics made a lot of instances unreachable, leading to lots of deployments to honor the contract (e.g. number of minimum instances) +- VMs that were under memory pressure were usually unresponsive and so they were (correctly) seen as unreachable +- VMs that were on overloaded hypervisors might have also been slowed down due to the high CPU usage of the hypervisor. Those VMs had less CPU time to do their usual operations, making their load average increase. Our monitoring took that as an increase of CPU usage and they were ordered to upscale if they were configured to +- Some of our monitoring instances took more time to do their checks because they were overloaded as well. Because the monitoring data was missing due to late checks, it was interpreted as lack of data and so as unreachable VMs, triggering more Monitoring/Unreachable events +- Customers seeing their service unavailable are trying to trigger redeployment of their applications, which adds more scheduling tasks on the infrastructure Our monitoring infrastructure, by observation of many key metrics (CPU Loads, CPU Steal, Availability, health checks, etc.) triggered a significantly higher rate of deployments, which at the time, wasn’t helping both to chill down the control plane already processing at the best it could and the overall infrastructure already overloaded. ### Soothing the control plane -Once we understood some Virtual Machines (VMs) were inducing an abnormal high load on Hypervisors (HVs), we started to filter out machines that could help to free some resources. All non critical services have been shut down like the Observability platform: We stopped the _Long Term Storage for Observability_ metrics data (our Warp10/FoundationDB cluster). Critical services benefit from In Memory Observability (Warp10 in memory standalones), so this was acceptable to preserve all available resources. +Once we understood some Virtual Machines (VMs) were inducing an abnormal high load on Hypervisors (HVs), we started to filter out machines that could help to free some resources. All non critical services have been shut down like the Observability platform: We stopped the *Long Term Storage for Observability* metrics data (our Warp10/FoundationDB cluster). Critical services benefit from In Memory Observability (Warp10 in memory standalones), so this was acceptable to preserve all available resources. Overloaded VMs were collected, sorted and it appeared they shared a common pattern: they were small VMs with few memory resources. The reason has been covered a few lines above. The process after identifying them is obviously to kill them one by one by the SRE team. Nothing fancy here, but soon we started to regain control over all hypervisors. @@ -611,34 +611,34 @@ Overloaded VMs were collected, sorted and it appeared they shared a common patte We’ve learnt many lessons this Friday and we already identified many enhancements we will give to the platform. Sure this kind of incident occurring during a summer holiday period is tricky, but we escalated the problem quickly to call people on holiday as soon as 13:00 CEST. We identified two groups of enhancements. A first one, measurable, and identified as a key infrastructure management where some of the measurement are : -* Pulsar Broker health -* Pulsar Cluster health -* Hypervisor load -* Hypervisor I/O -* Hypervisor reboot (Kernel Panic) -* Zombies VM -* RabbitMQ Broker health -* RabbitMQ Cluster health -* Deployment queue consumption +- Pulsar Broker health +- Pulsar Cluster health +- Hypervisor load +- Hypervisor I/O +- Hypervisor reboot (Kernel Panic) +- Zombies VM +- RabbitMQ Broker health +- RabbitMQ Cluster health +- Deployment queue consumption These measurements give us KPIs to measure where we've been operationally good, and where we have lacked some visibility or reaction. We qualified them with four inputs: -* Time of event -* Time of detection -* Time of decision -* Time of resolution +- Time of event +- Time of detection +- Time of decision +- Time of resolution This factual approach helped to challenge ourselves on established rules and showed we can improve or rethink some part of the infrastructure. Other topics raised but without KPI analysis: -* Dealing with too many monitoring alerts -* Backup Bastion — a regional bastion is vulnerable -* Control Plane recovery process -* Correlation between monitoring and Orchestration -* Fault domain separation vs multi tenancy -* Rethink for runtime images and the role of the hypervisor -* Isolation for waved release upgrades (Pulsar) +- Dealing with too many monitoring alerts +- Backup Bastion — a regional bastion is vulnerable +- Control Plane recovery process +- Correlation between monitoring and Orchestration +- Fault domain separation vs multi tenancy +- Rethink for runtime images and the role of the hypervisor +- Isolation for waved release upgrades (Pulsar) All the above questions and KPIs convert to actionable actions for teams that you may question at any time. @@ -646,17 +646,17 @@ All the above questions and KPIs convert to actionable actions for teams that yo Following immediate actions, we created a lot of issues to avoid the same behavior in the future: -* Runtime image redesign / better hypervisor integration -* Regionalized observability for redundancy -* Better profiling for small VMs : - * Vector configuration - * `buffer.max_events` / `buffer.max_size` (must be at least 256Mo if `type=disk`) - * `buffer.type` `memory` (default) → switch to `disk` - * `buffer.when_full` `block` (default) → `drop_newest` - * Journald verbosity and `SystemMaxUse` - * Cgroups restrictions for better process control - * I/O restrictions for VM manager -* Better zombies VM detection +- Runtime image redesign / better hypervisor integration +- Regionalized observability for redundancy +- Better profiling for small VMs : + - Vector configuration + - `buffer.max_events` / `buffer.max_size` (must be at least 256Mo if `type=disk`) + - `buffer.type` `memory` (default) → switch to `disk` + - `buffer.when_full` `block` (default) → `drop_newest` + - Journald verbosity and `SystemMaxUse` + - Cgroups restrictions for better process control + - I/O restrictions for VM manager +- Better zombies VM detection We don’t face a lot of outages and big issues, because by design, we strive to do a good job of running production applications for our customers. As we grow, challenges grow, and we’re committed to achieve great production SLA because it’s our DNA, our mission: running your production workloads. diff --git a/content/postmortem/2025-03-03.md b/content/postmortem/2025-03-03.md index 3ebc4c38c..2d2dc24cd 100644 --- a/content/postmortem/2025-03-03.md +++ b/content/postmortem/2025-03-03.md @@ -13,111 +13,111 @@ On Monday, March 3rd, 2025 Clever Cloud’s platform became unstable, leading to These events occurred because: -* A power outage on one of our data center providers caused a temporary disruption of our services on the PAR6 Availability Zone (AZ) from EU-FR-1 at 14:55 CET -* As an indirect consequence, networking traffic was perturbed, the infrastructure experienced I/O and memory pressure between 14:58 and 16:30 CET leading to instabilities and downtime. +- A power outage on one of our data center providers caused a temporary disruption of our services on the PAR6 Availability Zone (AZ) from EU-FR-1 at 14:55 CET +- As an indirect consequence, networking traffic was perturbed, the infrastructure experienced I/O and memory pressure between 14:58 and 16:30 CET leading to instabilities and downtime. Impacts were observed for customers relying on the EU-FR-1 (PAR) region, and remote zones depending on the EU-FR-1 control plane (GOV, OVHcloud, Scaleway, and Oracle) and some privates zones. On-premise zones weren’t impacted. -Status were communicated through : https://www.clevercloudstatus.com/incidents/959 +Status were communicated through : -| Product | Impact | -|---------|--------| -| Runtimes | Applications may have experienced performance degradation and traffic issues due to impacted hypervisors and load balancers | -| Runtimes deployment | The incident prevented new deployments, upscaling and downscaling operations from succeeding due to the Deployment API being unreachable | -| Databases (MySQL, PostgreSQL, Redis, MongoDB) | The incident may have caused performance degradation or unavailability for customers whose databases were running on affected hypervisors. Data loss did not occur | +| Product | Impact | +| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Runtimes | Applications may have experienced performance degradation and traffic issues due to impacted hypervisors and load balancers | +| Runtimes deployment | The incident prevented new deployments, upscaling and downscaling operations from succeeding due to the Deployment API being unreachable | +| Databases (MySQL, PostgreSQL, Redis, MongoDB) | The incident may have caused performance degradation or unavailability for customers whose databases were running on affected hypervisors. Data loss did not occur | | Cluster services (Elastic, Couchbase, Cassandra, …) | The service may have been maintained on some nodes, depending on the infrastructure pressure and the technology used. While some clusters were impacted during the incident, others were not | -| VPN / IPSec | Most of the VPN endpoints were unavailable during the incident | -| Observability | The service has successfully recovered from the electrical issue, and logs were available | +| VPN / IPSec | Most of the VPN endpoints were unavailable during the incident | +| Observability | The service has successfully recovered from the electrical issue, and logs were available | ## Timeline -| Timeline | Component | Description | -|-----------|-----------|-------------| -| 14:55 CET | Infrastructure | Power outage at PAR6 (Green Data) data center | -| 14:56 CET | Load Balancers | Monitoring advertises the loss of a load balancer - 1st event | -| 14:56 CET | Infrastructure | Monitoring shows a global incident reporting many unavailabilities | -| 14:57 CET | Infrastructure | Infrastructure team opens a crisis conference | -| 14:58 CET | Infrastructure | Power is back, servers and network equipment are restarting (confirmed later by data center incident report) | -| 14:58 CET | Customers | Support team received notification from external monitoring and customers that some websites are down (Console, admin, etc.) | -| 14:58 CET | Customers | Tech team reported that GitLab is also unavailable | -| 14:58 CET | Infrastructure | Tech team created an incident canal #incident-03-03-2025 | -| 15:00 CET | Infrastructure | Infrastructure team summoned others on call teams and members on canal #incident-03-03-2025 | -| 15:02 CET | Infrastructure | Hypervisors and network equipment have rebooted | -| 15:02 CET | Infrastructure | Several PAR6 hypervisors (12 of them) are indicating that their RAID array are rebuilding | -| 15:05 CET | Infrastructure | Reporting that AZ Monitoring is under heavy load | -| 15:06 CET | Infrastructure | Infrastructure team confirmed that we have retrieved every hypervisors on PAR6 since at least 4 minutes to 6 minutes | -| 15:08 CET | Network | A network provider hosted in PAR6 reports 2 powerline cuts | -| 15:10 CET | Infrastructure | Report from one of our network provider confirming the electrical outage | -| 15:14 CET | Messaging | Pulsar is back up online | -| 15:14 CET | Observability | Pulsar in-memory observability didn't restart properly | -| 15:18 CET | Observability | Pulsar in-memory observability is online | -| 15:19 CET | Infrastructure | A RabbitMQ node came back to the cluster after we fixed the communication between the hypervisor and the queue | -| 15:27 CET | DBaaS | Main addon databases clusters is correctly restarting | -| 15:27 CET | DBaaS | Check the availability of internal databases clusters | -| 15:29 CET | DBaaS | DBaaS team start to handle manually all ciphered databases which are still down (due to an unavailabilty of the addon provider APIs) | -| 15:30 CET | Infrastructure | Seeing a lot of WireGuard connectivity issues on multiple distributed clusters | -| 15:30 CET | Infrastructure | Asked to priorise GitLab databases restarts | +| Timeline | Component | Description | +| --------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 14:55 CET | Infrastructure | Power outage at PAR6 (Green Data) data center | +| 14:56 CET | Load Balancers | Monitoring advertises the loss of a load balancer - 1st event | +| 14:56 CET | Infrastructure | Monitoring shows a global incident reporting many unavailabilities | +| 14:57 CET | Infrastructure | Infrastructure team opens a crisis conference | +| 14:58 CET | Infrastructure | Power is back, servers and network equipment are restarting (confirmed later by data center incident report) | +| 14:58 CET | Customers | Support team received notification from external monitoring and customers that some websites are down (Console, admin, etc.) | +| 14:58 CET | Customers | Tech team reported that GitLab is also unavailable | +| 14:58 CET | Infrastructure | Tech team created an incident canal #incident-03-03-2025 | +| 15:00 CET | Infrastructure | Infrastructure team summoned others on call teams and members on canal #incident-03-03-2025 | +| 15:02 CET | Infrastructure | Hypervisors and network equipment have rebooted | +| 15:02 CET | Infrastructure | Several PAR6 hypervisors (12 of them) are indicating that their RAID array are rebuilding | +| 15:05 CET | Infrastructure | Reporting that AZ Monitoring is under heavy load | +| 15:06 CET | Infrastructure | Infrastructure team confirmed that we have retrieved every hypervisors on PAR6 since at least 4 minutes to 6 minutes | +| 15:08 CET | Network | A network provider hosted in PAR6 reports 2 powerline cuts | +| 15:10 CET | Infrastructure | Report from one of our network provider confirming the electrical outage | +| 15:14 CET | Messaging | Pulsar is back up online | +| 15:14 CET | Observability | Pulsar in-memory observability didn't restart properly | +| 15:18 CET | Observability | Pulsar in-memory observability is online | +| 15:19 CET | Infrastructure | A RabbitMQ node came back to the cluster after we fixed the communication between the hypervisor and the queue | +| 15:27 CET | DBaaS | Main addon databases clusters is correctly restarting | +| 15:27 CET | DBaaS | Check the availability of internal databases clusters | +| 15:29 CET | DBaaS | DBaaS team start to handle manually all ciphered databases which are still down (due to an unavailabilty of the addon provider APIs) | +| 15:30 CET | Infrastructure | Seeing a lot of WireGuard connectivity issues on multiple distributed clusters | +| 15:30 CET | Infrastructure | Asked to priorise GitLab databases restarts | | 15:33 CET | Infrastructure | Some hypervisors are overloaded, probably due to Vector/Telegraf not being able to push data to Warp10/Pulsar. Started to list all VMs with CPU load > 1 to relieve the infrastructure | -| 15:37 CET | Infrastructure | Check if Ceph/Cellar are okay. Some daemons seem to hang and 1/4 of OSD are still down on PAR6, requests are served correctly | -| 15:38 CET | DBaaS | Asked DBaaS team to prioritize Clever Cloud's GitLab databases | -| 15:38 CET | Infrastructure | Listed all applications with VMs CPU load > 1, starting to kill them to relieve the infrastructure | -| 15:42 CET | DBaaS | Databases reboot are still lock on some Hypervisor due to a network issue | -| 15:42 CET | Infrastructure | Fixed a network setup issue on a specific Hypervisor | -| 15:45 CET | Messaging | Listed all GitLab and Premium services databases for DBaaS team | -| 15:45 CET | Observability | A global maintenance is set on all monitoring servers in EU-FR-1 to avoid flooding the Orchestration core API with redeploy events | -| 15:45 CET | DBaaS | Asked to reboot PostgreSQL add-on provider in priority | -| 15:49 CET | Deployments | We lacked 2.8k VMs on the infrastructure | -| 15:52 CET | Infrastructure | Some deployments are failing on PAR6 due to unbound on some HV not restarted, no DNS available | -| 15:54 CET | Observability | See timeouts on Vector logs when connecting to Pulsar | -| 15:55 CET | Infrastructure | Unlock first HVs to allow databases reboot (HV iptables rules) | -| 15:56 CET | Messaging | We hit the max connections threshold on Pulsar | -| 15:56 CET | Infrastructure | Vector logs timeouts fixed, by corrective iptables on hypervisor | -| 15:59 CET | Infrastructure | All Hypervisor iptables generated are restored on PAR6 HVs and services have been restarted | -| 16:00 CET | DBaaS | Can now repair databases down impacted by hypervisors network issues | -| 16:03 CET | Infrastructure | Connectivity issues due to conntrack issue (need to purge conntrack table) | -| 16:05 CET | Customers | Some load balancers are unsynced due to RabbitMQ crash | -| 16:05 CET | Load Balancers | Start the rolling restart of unsynced load balancers | -| 16:06 CET | Infrastructure | Some deployments are still failing on PAR6 due to WireGuard problems with the DNS recursor of the AZ, the recursor is dropped from unbound / resolv.conf of all PAR6 hypervisors | -| 16:07 CET | Load Balancers | For some applications it's not a load balancer issue, but the application have no instances running | -| 16:08 CET | Infrastructure | Dropped UDP conntracks on the hypervisor of PAR6 DNS recursor to recover WireGuard mesh topologies | -| 16:19 CET | Messaging | WireGuard issues on BookKeeper cluster for Pulsar | -| 16:28 CET | Deployments | Purge delayed alerts on Zabbix monitoring | -| 16:30 CET | Deployments | Restart all applications without any VMs | -| 16:34 CET | DBaaS | Elasticsearch add-ons repair is locked due to the unavailability of our LDAP | -| 16:34 CET | Deployments | Reboot of Keycloack | -| 16:42 CET | Customers | Nodes of Couchbase have been reintroduced in their cluster | -| 16:45 CET | Infrastructure | Finished killing overloaded VMs | -| 16:47 CET | Deployments | Stop instances that are blocked and have failed their deployments and redeploy the application | -| 16:47 CET | Deployments | Fix a Ruby error on bastion that prevent to redeploy application | -| 16:58 CET | Deployments | Stacking semaphores in BDS Orchestrator | -| 16:58 CET | Infrastructure | Network issue on Warp10 FoundationDB | -| 17:00 CET | Deployments | Disabled HA replication of some queues to speed up RabbitMQ | -| 17:17 CET | DBaaS | Recovery of our LDAP enable an Elasticsearch add-ons recovery | -| 17:18 CET | Load Balancers | Restart all add-on providers to be able to properly reload last load balancers | -| 17:20 CET | Load Balancers | End the rolling restart of all impacted load balancers at Clever Cloud | -| 17:28 CET | Observability | Zabbix on PAR8 back in action | -| 17:31 CET | Deployments | Still 1.3k VMs to deploy | -| 17:35 CET | Infrastructure | Two FoundationDB's VMs could not be started because the NVMe was unavailable | -| 17:37 CET | Deployments | Increased concurrent deployments from 300 to 400 to increase recovery speed | -| 17:50 CET | Observability | Cleaning up old hosts in Paris Monitoring to avoid sending too much outdated monitoring events | -| 17:57 CET | Observability | Zabbix on PAR7 back in action | -| 18:00 CET | Deployments | Started to restart all applications still seen as unavailable by Monitoring | -| 18:05 CET | Infrastructure | Restart Sydney orchestration that are unsynced | -| 18:05 CET | Observability | Zabbix on PAR6 back in action | -| 18:18 CET | Infrastructure | Force to stop all VMs in stopping status | -| 18:45 CET | Observability | Remove the maintenance mode of Monitoring | -| 18:53 CET | Deployments | Deploy last application in failing deployment state | -| 18:54 CET | Infrastructure | Investigating under-replicated Kafka partitions on Warp10 | -| 18:55 CET | DBaaS | All databases are up and running | -| 19:00 CET | DBaaS | Some Elasticsearch are down again (due to an heavy resources usage on recovery) | -| 19:11 CET | Deployments | Finished to restart all applications still seen as unavailable by Zabbix | -| 19:12 CET | Infrastructure | Starting to recover the Ceph cluster, 2 physical machines don't have any network, one has a network configuration issue | -| 19:16 CET | Infrastructure | Start the recovery of Ceph cluster | -| 19:23 CET | Core APIs | Restart of Logs API | -| 19:35 CET | DBaaS | All add-ons are OK | -| 20:25 CET | Infrastructure | All Ceph machines are correctly restarted, cluster is recovering 0.3% of degraded objects | -| 20:48 CET | Infrastructure | Ceph cluster has recovered degraded objects, HEALTH_OK | -| 21:20 CET | DBaaS | Last replica down identified was rebuid | +| 15:37 CET | Infrastructure | Check if Ceph/Cellar are okay. Some daemons seem to hang and 1/4 of OSD are still down on PAR6, requests are served correctly | +| 15:38 CET | DBaaS | Asked DBaaS team to prioritize Clever Cloud's GitLab databases | +| 15:38 CET | Infrastructure | Listed all applications with VMs CPU load > 1, starting to kill them to relieve the infrastructure | +| 15:42 CET | DBaaS | Databases reboot are still lock on some Hypervisor due to a network issue | +| 15:42 CET | Infrastructure | Fixed a network setup issue on a specific Hypervisor | +| 15:45 CET | Messaging | Listed all GitLab and Premium services databases for DBaaS team | +| 15:45 CET | Observability | A global maintenance is set on all monitoring servers in EU-FR-1 to avoid flooding the Orchestration core API with redeploy events | +| 15:45 CET | DBaaS | Asked to reboot PostgreSQL add-on provider in priority | +| 15:49 CET | Deployments | We lacked 2.8k VMs on the infrastructure | +| 15:52 CET | Infrastructure | Some deployments are failing on PAR6 due to unbound on some HV not restarted, no DNS available | +| 15:54 CET | Observability | See timeouts on Vector logs when connecting to Pulsar | +| 15:55 CET | Infrastructure | Unlock first HVs to allow databases reboot (HV iptables rules) | +| 15:56 CET | Messaging | We hit the max connections threshold on Pulsar | +| 15:56 CET | Infrastructure | Vector logs timeouts fixed, by corrective iptables on hypervisor | +| 15:59 CET | Infrastructure | All Hypervisor iptables generated are restored on PAR6 HVs and services have been restarted | +| 16:00 CET | DBaaS | Can now repair databases down impacted by hypervisors network issues | +| 16:03 CET | Infrastructure | Connectivity issues due to conntrack issue (need to purge conntrack table) | +| 16:05 CET | Customers | Some load balancers are unsynced due to RabbitMQ crash | +| 16:05 CET | Load Balancers | Start the rolling restart of unsynced load balancers | +| 16:06 CET | Infrastructure | Some deployments are still failing on PAR6 due to WireGuard problems with the DNS recursor of the AZ, the recursor is dropped from unbound / resolv.conf of all PAR6 hypervisors | +| 16:07 CET | Load Balancers | For some applications it's not a load balancer issue, but the application have no instances running | +| 16:08 CET | Infrastructure | Dropped UDP conntracks on the hypervisor of PAR6 DNS recursor to recover WireGuard mesh topologies | +| 16:19 CET | Messaging | WireGuard issues on BookKeeper cluster for Pulsar | +| 16:28 CET | Deployments | Purge delayed alerts on Zabbix monitoring | +| 16:30 CET | Deployments | Restart all applications without any VMs | +| 16:34 CET | DBaaS | Elasticsearch add-ons repair is locked due to the unavailability of our LDAP | +| 16:34 CET | Deployments | Reboot of Keycloack | +| 16:42 CET | Customers | Nodes of Couchbase have been reintroduced in their cluster | +| 16:45 CET | Infrastructure | Finished killing overloaded VMs | +| 16:47 CET | Deployments | Stop instances that are blocked and have failed their deployments and redeploy the application | +| 16:47 CET | Deployments | Fix a Ruby error on bastion that prevent to redeploy application | +| 16:58 CET | Deployments | Stacking semaphores in BDS Orchestrator | +| 16:58 CET | Infrastructure | Network issue on Warp10 FoundationDB | +| 17:00 CET | Deployments | Disabled HA replication of some queues to speed up RabbitMQ | +| 17:17 CET | DBaaS | Recovery of our LDAP enable an Elasticsearch add-ons recovery | +| 17:18 CET | Load Balancers | Restart all add-on providers to be able to properly reload last load balancers | +| 17:20 CET | Load Balancers | End the rolling restart of all impacted load balancers at Clever Cloud | +| 17:28 CET | Observability | Zabbix on PAR8 back in action | +| 17:31 CET | Deployments | Still 1.3k VMs to deploy | +| 17:35 CET | Infrastructure | Two FoundationDB's VMs could not be started because the NVMe was unavailable | +| 17:37 CET | Deployments | Increased concurrent deployments from 300 to 400 to increase recovery speed | +| 17:50 CET | Observability | Cleaning up old hosts in Paris Monitoring to avoid sending too much outdated monitoring events | +| 17:57 CET | Observability | Zabbix on PAR7 back in action | +| 18:00 CET | Deployments | Started to restart all applications still seen as unavailable by Monitoring | +| 18:05 CET | Infrastructure | Restart Sydney orchestration that are unsynced | +| 18:05 CET | Observability | Zabbix on PAR6 back in action | +| 18:18 CET | Infrastructure | Force to stop all VMs in stopping status | +| 18:45 CET | Observability | Remove the maintenance mode of Monitoring | +| 18:53 CET | Deployments | Deploy last application in failing deployment state | +| 18:54 CET | Infrastructure | Investigating under-replicated Kafka partitions on Warp10 | +| 18:55 CET | DBaaS | All databases are up and running | +| 19:00 CET | DBaaS | Some Elasticsearch are down again (due to an heavy resources usage on recovery) | +| 19:11 CET | Deployments | Finished to restart all applications still seen as unavailable by Zabbix | +| 19:12 CET | Infrastructure | Starting to recover the Ceph cluster, 2 physical machines don't have any network, one has a network configuration issue | +| 19:16 CET | Infrastructure | Start the recovery of Ceph cluster | +| 19:23 CET | Core APIs | Restart of Logs API | +| 19:35 CET | DBaaS | All add-ons are OK | +| 20:25 CET | Infrastructure | All Ceph machines are correctly restarted, cluster is recovering 0.3% of degraded objects | +| 20:48 CET | Infrastructure | Ceph cluster has recovered degraded objects, HEALTH_OK | +| 21:20 CET | DBaaS | Last replica down identified was rebuid | ## Analysis @@ -134,13 +134,16 @@ We're expecting a better postmortem from our data center operator. Our network relies on multiple transits to ensure both a good Internet connectivity and a good resilience. These transits are spread among three Points of Presence (PoP). During the restart of networking routers, there appears to be an issue with accessing the region via public connectivity, despite these multiple transits. Our observability system was unfortunately located on the lost AZ, so we lost the flows to analyze the traffic during the power cut. Known Issues: + - In some cases, when a router reboots, it may start receiving traffic before it knows where to route it. A specific starting sequence may be enforced to avoid this behavior. #### Possible Causes of Downtime + - A transit partner did not notice that the session was down before the BGP session timed out (a few minutes), so traffic continued to be routed through them even though we were no longer there to handle it. Another transit partner continued to announce our prefixes even though they couldn't route the traffic to our network. - There is a 3-minutes timeout on the BGP sessions of the routers, with no Bidirectional Forwarding Detection (BFD) to accelerate convergence. #### Unclear Areas + - In the nominal situation, most of our traffic is gathered at TH2 POP, so we shouldn't have seen such a significant impact. However, most of our traffic exits through another transit partner through the incriminated AZ (correlate with the one of the possible cause of downtime) - Did the other routers still have announcements (received from the power cut AZ routers) when they received traffic to send to the internet, because the BGP sessions hadn't yet timed out? (correlate with the one of the possible cause of downtime) @@ -172,6 +175,7 @@ Also, a few hours before the incident, we deployed multiple enhanced images, wit Initially, we implemented an orchestration system based on an offering model (similar to Apache Mesos), which provided a solid foundation for managing our services. In an offering model, an orchestrator manages the allocation and deallocation of resources, such as virtual machines (VMs), to ensure efficient utilization and meet service demands. However, the orchestrator can become stuck due to semaphore locking issues during VM assignment for several reasons: + - **Resource Contention:** Semaphores are used to control access to shared resources, ensuring that only a limited number of processes can access them simultaneously. If multiple VMs are attempting to acquire the same resource concurrently, contention can occur, leading to semaphore locks. This can cause the orchestrator to wait indefinitely if the locks are not released promptly. - **Deadlocks:** A deadlock situation arises when two or more processes are each holding a resource and waiting for the other to release a resource they need. In the context of VM assignment, if the orchestrator and other processes are waiting for each other to release semaphores, it can result in a deadlock, causing the orchestrator to become stuck. - **Improper Semaphore Management:** If semaphores are not properly initialized, acquired, or released, it can lead to locking issues. For instance, if a semaphore is not released after a VM assignment operation completes, other processes waiting for that semaphore will remain blocked, including the orchestrator. @@ -193,22 +197,22 @@ However, recognizing the need for enhanced capabilities and a broader range of p ### Runtimes - - #856 Enforce guard I/O rules at Hypervisor level - - #857 Enhance observability for small profile VM memory pressure +- #856 Enforce guard I/O rules at Hypervisor level +- #857 Enhance observability for small profile VM memory pressure - ### Networking +### Networking - - #832 Ensure OSPF/BGP convergence times on routers - - #898 Enhance boot sequence process on routers - - #899 Ensure a good resiliency for border routing BGP announces +- #832 Ensure OSPF/BGP convergence times on routers +- #898 Enhance boot sequence process on routers +- #899 Ensure a good resiliency for border routing BGP announces - ### Orchestration +### Orchestration actions - - #900 Ensure a failsafe VM restart capability - - #901 Enhance hypervisor sequence of start process - - #902 Ensure a better network configuration compliance across upgrades - - #864 Better observability for unresponsive VM - - #903 Improve AZ monitoring resiliency +- #900 Ensure a failsafe VM restart capability +- #901 Enhance hypervisor sequence of start process +- #902 Ensure a better network configuration compliance across upgrades +- #864 Better observability for unresponsive VM +- #903 Improve AZ monitoring resiliency ### Conclusion diff --git a/content/postmortem/2025-10-09.md b/content/postmortem/2025-10-09.md index a6661bddc..218ab5d32 100644 --- a/content/postmortem/2025-10-09.md +++ b/content/postmortem/2025-10-09.md @@ -14,43 +14,43 @@ We lost a first hypervisor due to high memory pressure and at the same time ther Then, this put pressure on the Paris region, which caused another hypervisor to enter the same state. We lost critical services in the meantime due to infrastructure that was already under heavy load. -Status were communicated through : https://www.clevercloudstatus.com/incidents/1020 - -### Timeline - -| Time | Description | -| ---------------- | ------------------------------------------------------------ | -| 2025-10-09 07:10 | Monitoring of time series systems fails to work properly, distributed systems oncall operator is paged | -| 2025-10-09 07:40 | Time series systems fails to receive ingress traffic | -| 2025-10-09 07:59 | ALL time series systems load balancers are stuck due to too much connections | -| 2025-10-09 08:01 | Distributed systems operator raises the alert to Level 2 | -| 2025-10-09 08:03 | On of the hypervisor is detected as down | -| 2025-10-09 08:12 | Orchestration operations are limited to limit a potential domino effect and reduce deployment pressure on infrastructure | -| 2025-10-09 08:15 | Restart of time series systems ingresses / an hypervisor experiencing high load (2300), memory pressure identified | -| 2025-10-09 08:17 | Passing the faulty hypervisor to CLI for management | -| 2025-10-09 08:18 | Metrics are getting ingested by time series systems again (1/5 of nominal traffic) | -| 2025-10-09 08:23 | Enable time series systems load balancer datapoints sampling (blackhole) on 50% of traffic | -| 2025-10-09 08:35 | Root cause identified: RAM capacity issue causing load spike on hypervisors | -| 2025-10-09 08:37 | Faulty hypervisor is rebooted successfully (uptime: 2025-10-09 06:37:06 UTC) | -| 2025-10-09 08:40 | Domino effect detected: an hypervisor is showing signs of stress | -| 2025-10-09 08:43 | Multiple databases impacted (Down), the full Database Team is escalated | -| 2025-10-09 08:46 | Time series systems is at 75% of nominal ingress traffic | -| 2025-10-09 09:00 | Time series systems is fully operational & back to nominal traffic | -| 2025-10-09 09:09 | Addon provider Mongo is back online | -| 2025-10-09 09:10 | On a faulty hypervisor , all databases are up again | -| 2025-10-09 09:14 | On the second faulty hypervisor, all databases are up again | +Status were communicated through : + +## Timeline + +| Time | Description | +| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 2025-10-09 07:10 | Monitoring of time series systems fails to work properly, distributed systems oncall operator is paged | +| 2025-10-09 07:40 | Time series systems fails to receive ingress traffic | +| 2025-10-09 07:59 | ALL time series systems load balancers are stuck due to too much connections | +| 2025-10-09 08:01 | Distributed systems operator raises the alert to Level 2 | +| 2025-10-09 08:03 | On of the hypervisor is detected as down | +| 2025-10-09 08:12 | Orchestration operations are limited to limit a potential domino effect and reduce deployment pressure on infrastructure | +| 2025-10-09 08:15 | Restart of time series systems ingresses / an hypervisor experiencing high load (2300), memory pressure identified | +| 2025-10-09 08:17 | Passing the faulty hypervisor to CLI for management | +| 2025-10-09 08:18 | Metrics are getting ingested by time series systems again (1/5 of nominal traffic) | +| 2025-10-09 08:23 | Enable time series systems load balancer datapoints sampling (blackhole) on 50% of traffic | +| 2025-10-09 08:35 | Root cause identified: RAM capacity issue causing load spike on hypervisors | +| 2025-10-09 08:37 | Faulty hypervisor is rebooted successfully (uptime: 2025-10-09 06:37:06 UTC) | +| 2025-10-09 08:40 | Domino effect detected: an hypervisor is showing signs of stress | +| 2025-10-09 08:43 | Multiple databases impacted (Down), the full Database Team is escalated | +| 2025-10-09 08:46 | Time series systems is at 75% of nominal ingress traffic | +| 2025-10-09 09:00 | Time series systems is fully operational & back to nominal traffic | +| 2025-10-09 09:09 | Addon provider Mongo is back online | +| 2025-10-09 09:10 | On a faulty hypervisor , all databases are up again | +| 2025-10-09 09:14 | On the second faulty hypervisor, all databases are up again | | 2025-10-09 09:14 | DBaaS team is now fixing random databases that wen't down on the par8 datacenter (manly small encrypted plans). Reboot failing due to a local Out Of Memory (oom) | -| 2025-10-09 09:18 | On of four time series systems loadbalancer is not rebooting, due to missing base image | -| 2025-10-09 09:25 | Issue to reboot some databases (OOM before start). We restarted them in rescue and deactivated the logs to enable the reboot. | -| 2025-10-09 09:46 | All four time series systems loadbalancer are up again | -| 2025-10-09 09:55 | The rebooted databases without logs were correctly migrated, logs are now collected again | -| 2025-10-09 09:56 | An automated system reported an expired certificate on time series systems customer endpoint for ingress, half the traffic fails to reach time series systems | -| 2025-10-09 10:30 | two of the four time series systems load balancers are exposing expired certificates | -| 2025-10-09 10:39 | Time series systems expired certificates issue is fixed, back to nominal | -| 2025-10-09 10:43 | All databases impacted by the incident are up and running | -| 2025-10-09 10:46 | mass restart of supernova/supernova-http on all hypervisors via Ansible - RabbitMQ connection flapping detected | -| 2025-10-09 10:52 | Some databases were going down again, they are now fixed and migrated to resolve the issue | -| 2025-10-09 11:10 | proxy-manager / hayxorp / rabbitfrs restart across the infrastructure - verification that all services reconnected to RabbitMQ successfully | +| 2025-10-09 09:18 | On of four time series systems loadbalancer is not rebooting, due to missing base image | +| 2025-10-09 09:25 | Issue to reboot some databases (OOM before start). We restarted them in rescue and deactivated the logs to enable the reboot. | +| 2025-10-09 09:46 | All four time series systems loadbalancer are up again | +| 2025-10-09 09:55 | The rebooted databases without logs were correctly migrated, logs are now collected again | +| 2025-10-09 09:56 | An automated system reported an expired certificate on time series systems customer endpoint for ingress, half the traffic fails to reach time series systems | +| 2025-10-09 10:30 | two of the four time series systems load balancers are exposing expired certificates | +| 2025-10-09 10:39 | Time series systems expired certificates issue is fixed, back to nominal | +| 2025-10-09 10:43 | All databases impacted by the incident are up and running | +| 2025-10-09 10:46 | mass restart of supernova/supernova-http on all hypervisors via Ansible - RabbitMQ connection flapping detected | +| 2025-10-09 10:52 | Some databases were going down again, they are now fixed and migrated to resolve the issue | +| 2025-10-09 11:10 | proxy-manager / hayxorp / rabbitfrs restart across the infrastructure - verification that all services reconnected to RabbitMQ successfully | ## Analysis @@ -58,23 +58,25 @@ An mechanical incident in the Paris region created an unexpected memory pressure ### Actions -* Complete the deployment of oomd which will help to better handle memory pressure -* Add a monitoring disaster trigger to detect when a hypervisor stays under pressure for too long -* Fix small MySQL databases that were on error after the reboot during the incident -* Add TLS check on each warp10 load balancers for c2-warp10-clevercloud-customers.services.clever-cloud.com +- Complete the deployment of oomd which will help to better handle memory pressure +- Add a monitoring disaster trigger to detect when a hypervisor stays under pressure for too long +- Fix small MySQL databases that were on error after the reboot during the incident +- Add TLS check on each warp10 load balancers for c2-warp10-clevercloud-customers.services.clever-cloud.com -* New hardware installation will reduce pressure on infrastructure -* Configure time series systems ingress thresholds to deny traffic before instances become stuck +- New hardware installation will reduce pressure on infrastructure +- Configure time series systems ingress thresholds to deny traffic before instances become stuck ### Conclusion During the incident, our actions demonstrated a good reactivity, but also some potential enhancements. The infrastructure congestion has already been resolved on two stages: + - immediate capacity addition (done) - accelerate the two new AZ project with a shipment for january We also learnt on the behavior of machine health in case of cooling issue on a given availability zone (AZ), which never occurred before. Databases management is being enhanced with two strategic actions: + - volume capability that enable arbitrary restart on any other AZ - automatic failover for leader/follower setups diff --git a/content/postmortem/2026-03-19.md b/content/postmortem/2026-03-19.md index 3131f7045..9a2f19744 100644 --- a/content/postmortem/2026-03-19.md +++ b/content/postmortem/2026-03-19.md @@ -14,18 +14,18 @@ A memory pressure spike on ~15 Paris hypervisors, amplified by a placement algor The overloaded servers triggered OS memory reclaim (OOM), which consumed CPU and destabilized HAProxy load balancers — causing connectivity loss for hosted services and paralyzing internal automation (deployments, monitoring). Full recovery at 21:41 UTC. -Status updates were communicated through: https://www.clevercloudstatus.com/ +Status updates were communicated through: -### Timeline +## Timeline -| Time | Description | -| ---------------- | ------------------------------------------------------------ | -| 2026-03-19 15:20 | First degradation signals on internal services (VM registration, deployment service database access). No client impact yet. | -| 2026-03-19 15:50 | Degradations and outages visible for some clients. Services hosted on the most impacted hypervisors become unreachable. Deployment pipeline stops. | +| Time | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 2026-03-19 15:20 | First degradation signals on internal services (VM registration, deployment service database access). No client impact yet. | +| 2026-03-19 15:50 | Degradations and outages visible for some clients. Services hosted on the most impacted hypervisors become unreachable. Deployment pipeline stops. | | 2026-03-19 16:03 | Incident response team mobilized. Mitigation actions begin: stabilizing hypervisors under pressure, progressive restart of internal clusters (messaging, observability), partial deployment resumption. | -| 2026-03-19 17:30 | Full restoration of the deployment pipeline and observability. Continued recovery of impacted client services. | -| 2026-03-19 18:09 | All impacted client services restored. Residual alert cleanup begins. | -| 2026-03-19 21:41 | Service fully operational. All residual alerts cleared. | +| 2026-03-19 17:30 | Full restoration of the deployment pipeline and observability. Continued recovery of impacted client services. | +| 2026-03-19 18:09 | All impacted client services restored. Residual alert cleanup begins. | +| 2026-03-19 21:41 | Service fully operational. All residual alerts cleared. | ## Analysis @@ -37,13 +37,13 @@ The memory saturation triggered the OS memory reclaim mechanism, which itself is ### Actions -* Placement scoring function modified to smooth load ramp-up and eliminate the threshold effect -* Dedicated monitoring added to detect any recurrence of this behavior -* Temporary fixes deployed immediately during the incident to mitigate effects -* Partial algorithm redesign to guarantee smooth and optimal resource allocation under high load -* Enhanced observability for non-VM processes running on hypervisors (especially HAProxy instrumentation) -* Memory pressure resilience improvements prioritized and delivered within the following week -* Two new datacenters planned for the Paris region in Q2 2026 to increase capacity and reduce sensitivity to load spikes +- Placement scoring function modified to smooth load ramp-up and eliminate the threshold effect +- Dedicated monitoring added to detect any recurrence of this behavior +- Temporary fixes deployed immediately during the incident to mitigate effects +- Partial algorithm redesign to guarantee smooth and optimal resource allocation under high load +- Enhanced observability for non-VM processes running on hypervisors (especially HAProxy instrumentation) +- Memory pressure resilience improvements prioritized and delivered within the following week +- Two new datacenters planned for the Paris region in Q2 2026 to increase capacity and reduce sensitivity to load spikes ### Conclusion diff --git a/drafts/community.md b/drafts/community.md index 53c25b0ca..661cd1bc7 100644 --- a/drafts/community.md +++ b/drafts/community.md @@ -14,11 +14,11 @@ Engage in our Developer Community by seeking or providing advice on application ## Gitter -* English room: -* French room: +- English room: +- French room: ## Public IRC Channel We’re also available on IRC. You can join our channel: -* [#clevercloud on irc.freenode.net](irc://irc.freenode.net:6667/clevercloud) +- [#clevercloud on irc.freenode.net](irc://irc.freenode.net:6667/clevercloud) diff --git a/shared/kv-explorer.md b/shared/kv-explorer.md index 29ce77bec..730ab3fc4 100644 --- a/shared/kv-explorer.md +++ b/shared/kv-explorer.md @@ -16,6 +16,6 @@ At the bottom of the page, you'll find an integrated terminal that works like a KV Explorer is in Beta testing phase and we'll improve it over the coming months, thanks to your feedback and suggestions. -- [ Share your feedback on KV Explorer](https://github.com/CleverCloud/Community/discussions/categories/kv-explorer) +- [Share your feedback on KV Explorer](https://github.com/CleverCloud/Community/discussions/categories/kv-explorer) ![KV Explorer](/images/kv-explorer.webp "The KV Explorer tool in the Console") diff --git a/shared/redirectionio.md b/shared/redirectionio.md index 04dbcaa2d..e0450d95f 100644 --- a/shared/redirectionio.md +++ b/shared/redirectionio.md @@ -2,10 +2,10 @@ [Redirection.io](https://redirection.io) can help reduce HTTP traffic issues on your website. It gives complete control over how HTTP requests are handled, which helps make it SEO-friendly. You can link any application to a Redirection.io project by setting the following environment variables. Port allocation is managed automatically by [Request Flow](/doc/develop/request-flow/). -| Name | Description | Default value | -|-----------------------|------------------------------|--------------------------------| -| `CC_REDIRECTIONIO_PROJECT_KEY` | The Redirection.io project key | | -| `CC_REDIRECTIONIO_FORWARD_PORT` | Override the port Redirection.io forwards traffic to (optional) | | -| `CC_REDIRECTIONIO_INSTANCE_NAME` | The name of your application (optional) | | +| Name | Description | Default value | +| -------------------------------- | --------------------------------------------------------------- | ------------- | +| `CC_REDIRECTIONIO_PROJECT_KEY` | The Redirection.io project key | | +| `CC_REDIRECTIONIO_FORWARD_PORT` | Override the port Redirection.io forwards traffic to (optional) | | +| `CC_REDIRECTIONIO_INSTANCE_NAME` | The name of your application (optional) | | - [Learn more about Redirection.io](https://redirection.io/) diff --git a/shared/ruby.md b/shared/ruby.md index c2c192a5a..663dcc4e1 100644 --- a/shared/ruby.md +++ b/shared/ruby.md @@ -4,10 +4,10 @@ Be sure that: -* you push to the **master branch** -* you commit your `gems.locked` or `Gemfile.lock` file -* you have a `config.ru` file (this one is most of the time generated by rails) -* you have `gem puma` in your `Gemfile` (`puma` is the default application server from rails, when creating a new ruby application, an [environment variable](#setting-up-environment-variables-on-clever-cloud) is automatically added: `CC_RACKUP_SERVER=puma`) +- you push to the **master branch** +- you commit your `gems.locked` or `Gemfile.lock` file +- you have a `config.ru` file (this one is most of the time generated by rails) +- you have `gem puma` in your `Gemfile` (`puma` is the default application server from rails, when creating a new ruby application, an [environment variable](#setting-up-environment-variables-on-clever-cloud) is automatically added: `CC_RACKUP_SERVER=puma`) You need to provide a `gems.locked` or `Gemfile.lock` file. To do that ensure you have at least once run `bundle install` in your terminal at the root of your rails project. @@ -15,9 +15,9 @@ You need to provide a `gems.locked` or `Gemfile.lock` file. To do that ensure yo You can specify a Ruby version in your `gems.rb` or `Gemfile`, or set `CC_RUBY_VERSION=`, where `` can be, for example: -* "4" will select the greatest "4.x.y" version available. -* "4.0" will select the greatest "4.0.y" version available. -* "4.0.1" will select the "4.0.1" version. +- "4" will select the greatest "4.x.y" version available. +- "4.0" will select the greatest "4.0.y" version available. +- "4.0.1" will select the "4.0.1" version. If the given `` does not match any available version, your deployment will fail. If no version is specified, the latest 4.x version available on the image is used. Versions from the 4.x, 3.x and 2.x branches are available, but we recommend using an [officially supported version](https://www.ruby-lang.org/en/downloads/branches/). @@ -97,10 +97,10 @@ For other Rack applications, `STATIC_FILES_PATH` specifies the static files dire Only for Rails >= 5.2. -* Enable Active Storage for your application: `$ rails active_storage:install` then `$ rake db:migrate` -* Add `gem "aws-sdk-s3", require: false` to your Gemfile, run `$ bundle install` -* Add `config.active_storage.service = :clevercloud` in `config/environments/production.yml` -* Add in `config/storage.yml`: +- Enable Active Storage for your application: `$ rails active_storage:install` then `$ rake db:migrate` +- Add `gem "aws-sdk-s3", require: false` to your Gemfile, run `$ bundle install` +- Add `config.active_storage.service = :clevercloud` in `config/environments/production.yml` +- Add in `config/storage.yml`: ```yaml clevercloud: @@ -113,8 +113,8 @@ Only for Rails >= 5.2. force_path_style: true ``` -* In the clever cloud console create a Cellar S3 storage add-on, name it, link it to your rails application and create a bucket. -* In the environment variables section of your Ruby on Rails application on Clever Cloud add the following environment variables: +- In the clever cloud console create a Cellar S3 storage add-on, name it, link it to your rails application and create a bucket. +- In the environment variables section of your Ruby on Rails application on Clever Cloud add the following environment variables: ```bash CELLAR_ADDON_BUCKET_NAME="" @@ -130,7 +130,7 @@ Also, you are able to use a Filesystem Bucket to store your static files. Please NGINX settings can be configured with [environment variables](#setting-up-environment-variables-on-clever-cloud): -* `NGINX_READ_TIMEOUT`: the response timeout in seconds. (Default: 300) +- `NGINX_READ_TIMEOUT`: the response timeout in seconds. (Default: 300) #### Basic authentication @@ -140,11 +140,11 @@ If you need basic authentication, you can enable it using [environment variables NGINX settings can be configured further in `clevercloud/http.json`. All its fields are optional. -* `languages`: configure a default language and redirections -* `error_pages`: configure custom files for error pages -* `force_https`: automatically redirect HTTP traffic to HTTPS -* `aliases`: set up redirections -* `charset`: force a specific charset +- `languages`: configure a default language and redirections +- `error_pages`: configure custom files for error pages +- `force_https`: automatically redirect HTTP traffic to HTTPS +- `aliases`: set up redirections +- `charset`: force a specific charset ```json { @@ -170,8 +170,8 @@ Puma reads its configuration from the `config/puma.rb` file. See [the puma docum You can override this configuration with [environment variables](#setting-up-environment-variables-on-clever-cloud). Each of them, when specified, will be preferred over the setting from `config/puma.rb`. -* `CC_PUMA_WORKERS` overrides the number of workers (for example, `CC_PUMA_WORKERS=2`) -* `CC_PUMA_THREADS` overrides the number of threads per worker, can be a raw number or a range (for example, `CC_PUMA_THREADS=6` or `CC_PUMA_THREADS=4:8`) +- `CC_PUMA_WORKERS` overrides the number of workers (for example, `CC_PUMA_WORKERS=2`) +- `CC_PUMA_THREADS` overrides the number of threads per worker, can be a raw number or a range (for example, `CC_PUMA_THREADS=6` or `CC_PUMA_THREADS=4:8`) If they are not defined in the environment nor in `config/puma.rb` we will setup the values depending on the size of the scaler your application is running on. We also fill the `WEB_CONCURRENCY` and `RAILS_MAX_THREADS` environment variable if they are not present as they may be used by rails' puma configuration. diff --git a/shared/warp10-concepts.md b/shared/warp10-concepts.md index 65cce033d..8017cbe35 100644 --- a/shared/warp10-concepts.md +++ b/shared/warp10-concepts.md @@ -6,5 +6,5 @@ A GeoTime Serie (GTS) is defined by a `class` and some `labels`. They're indexed Warp 10 uses Warp Script. It's a stack based language using reverse polish notation. -* [Warp 1O documentation](https://www.warp10.io/doc/reference) -* [Warp Script documentation](https://www.warp10.io/content/03_Documentation/04_WarpScript) +- [Warp 1O documentation](https://www.warp10.io/doc/reference) +- [Warp Script documentation](https://www.warp10.io/content/03_Documentation/04_WarpScript) diff --git a/shared/warp10-content.md b/shared/warp10-content.md index 1087bfaaa..b582599ce 100644 --- a/shared/warp10-content.md +++ b/shared/warp10-content.md @@ -2,8 +2,8 @@ The platform's time unit is in **microsecond**. -* [Learn more about date functions](https://www.warp10.io/tags/date) -* [Learn more about time functions](https://www.warp10.io/tags/time) +- [Learn more about date functions](https://www.warp10.io/tags/date) +- [Learn more about time functions](https://www.warp10.io/tags/time) #### Duration is set by the followings symbols @@ -32,18 +32,18 @@ Builtin function: The followings limits are defined in Warp 10. The **soft** limit can be passed over by an [`AUTHENTICATE`](https://www.warp10.io/doc/AUTHENTICATE) operation. Operations over **soft limits** may be intensive. The **hard** limit is unsurpassable. -| WarpScript Operator | Warp 10 limit description | soft limit | hard limit | -| ------------------- | ------------------------------------------------------------- | ---------- | ---------- | -| MAXGTS | Maximum number of GTS which can be fetched | 10e5 | 5e7 | -| LIMIT | Maximum number of datapoints which can be fetched during a script execution | 10e6 | 10e7 | -| MAXBUCKETS | Maximum number of buckets which can be created by a call to BUCKETIZE | 10e5 | 50e5 | -| MAXDEPTH | Maximum depth (number of levels) of the execution stack | 5e3 | 5e3 | -| MAXLOOP | Maximum number of milliseconds which can be spent in a loop | 5e3 | 10e3 | -| MAXOPS | Maximum number of operations which can be performed during a single WarpScript execution | 5e6 | 5e7 | -| MAXSYMBOLS | Maximum number of simultaneous symbols which can be defined on the stack during a single WarpScript execution | 64 | 256 | -| MAXGEOCELLS | Maximum number of cells a GEOSHAPE | 10e3 | 10e4 | -| MAXPIXELS | Maximum size (in pixels) of images which can be created by PGraphics | 10e5 | 10e5 | -| MAXRECURSION | Maximum nesting depth of macro calls | 16 | 32 | +| WarpScript Operator | Warp 10 limit description | soft limit | hard limit | +| ------------------- | ------------------------------------------------------------------------------------------------------------- | ---------- | ---------- | +| MAXGTS | Maximum number of GTS which can be fetched | 10e5 | 5e7 | +| LIMIT | Maximum number of datapoints which can be fetched during a script execution | 10e6 | 10e7 | +| MAXBUCKETS | Maximum number of buckets which can be created by a call to BUCKETIZE | 10e5 | 50e5 | +| MAXDEPTH | Maximum depth (number of levels) of the execution stack | 5e3 | 5e3 | +| MAXLOOP | Maximum number of milliseconds which can be spent in a loop | 5e3 | 10e3 | +| MAXOPS | Maximum number of operations which can be performed during a single WarpScript execution | 5e6 | 5e7 | +| MAXSYMBOLS | Maximum number of simultaneous symbols which can be defined on the stack during a single WarpScript execution | 64 | 256 | +| MAXGEOCELLS | Maximum number of cells a GEOSHAPE | 10e3 | 10e4 | +| MAXPIXELS | Maximum size (in pixels) of images which can be created by PGraphics | 10e5 | 10e5 | +| MAXRECURSION | Maximum nesting depth of macro calls | 16 | 32 | ### Usage @@ -74,7 +74,7 @@ For a complete list of Telegraf classes and their descriptions, see [the classes Warp 10 provide a server side macro manager. It is a way to release some ready to use WarpScript. Hence, Clever Cloud provides some macros as helpers to avoid redundant and often need code. -* [Warp 10' macros documentations](https://www.warp10.io/content/03_Documentation/07_Extending_Warp_10/01_Server_side_macros) +- [Warp 10' macros documentations](https://www.warp10.io/content/03_Documentation/07_Extending_Warp_10/01_Server_side_macros) ### Consumption diff --git a/update-cli-reference.sh b/update-cli-reference.sh index 1f7d1446b..23899dec7 100755 --- a/update-cli-reference.sh +++ b/update-cli-reference.sh @@ -23,6 +23,8 @@ aliases: - /reference/clever-tools - /reference/clever-tools/getting_started --- + + """ echo "${front_matter}" > "${FILE}" From 469d235125a3abb19f7148ac79c49c37de0298e6 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 18:34:12 +0200 Subject: [PATCH 143/214] addons(cellar): document Object Lock --- content/doc/addons/cellar.md | 148 +++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/content/doc/addons/cellar.md b/content/doc/addons/cellar.md index d495ea348..12efd2cf4 100644 --- a/content/doc/addons/cellar.md +++ b/content/doc/addons/cellar.md @@ -841,6 +841,154 @@ When versioning is enabled, the newly added object is automatically provided wit {{< /tabs >}} +## Object Lock + +Cellar supports [S3 Object Lock](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html), which protects individual object versions from permanent deletion or modification with a [write-once-read-many (WORM)](https://en.wikipedia.org/wiki/Write_once_read_many) model. It can reduce the risk of accidental or malicious deletion and enforce data-retention policies. + +Using Object Lock requires two steps. First, enable the feature when you create the bucket; Cellar doesn't allow you to add it later. This automatically activates [versioning](#versioning), which you can no longer suspend. Second, choose how to protect object versions: + +- Set a default retention period for every new version +- Apply a retention period or legal hold only to selected versions + +Until you complete this second step, Object Lock doesn't protect any objects. + +### Create a bucket with Object Lock + +The following examples create `my-locked-bucket` with Object Lock enabled. Replace the bucket name with your own value. + +{{< tabs >}} + + {{< tab name="s3cmd" icon="terminal" >}} + + This command assumes you have [configured s3cmd](#with-s3cmd). s3cmd doesn't provide a dedicated option for Object Lock at bucket creation, but you can add a specific header to enable it: + + ```bash + s3cmd --add-header=x-amz-bucket-object-lock-enabled:true mb s3://my-locked-bucket + ``` + + {{< /tab >}} + + {{< tab name="MinIO" icon="minio" >}} + + This command assumes you have [configured a MinIO alias](#activate-versioning-with-minio) named `my-cellar`: + + ```bash + mc mb --with-lock my-cellar/my-locked-bucket + ``` + + See the MinIO documentation for [`mc mb`](https://docs.min.io/community/minio-object-store/reference/minio-mc/mc-mb.html). + + {{< /tab >}} + + {{< tab name="AWS CLI" icon="aws" >}} + + This command assumes you have [configured your AWS CLI credentials](#with-aws-cli): + + ```bash + aws s3api create-bucket \ + --bucket my-locked-bucket \ + --object-lock-enabled-for-bucket \ + --endpoint-url https://cellar-c2.services.clever-cloud.com + ``` + + See the AWS CLI documentation for [`create-bucket`](https://docs.aws.amazon.com/cli/latest/reference/s3api/create-bucket.html). + + {{< /tab >}} + +{{< /tabs >}} + +### Set a default retention period + +The following examples complete the configuration by protecting every new object version for 30 days in Governance mode. s3cmd can't configure a default retention period, so use MinIO or AWS CLI for this step. + +{{< tabs >}} + + {{< tab name="MinIO" icon="minio" >}} + + ```bash + mc retention set --default governance 30d my-cellar/my-locked-bucket + mc retention info --default my-cellar/my-locked-bucket + ``` + + See the MinIO documentation for [`mc retention`](https://docs.min.io/community/minio-object-store/reference/minio-mc/mc-retention.html). + + {{< /tab >}} + + {{< tab name="AWS CLI" icon="aws" >}} + + ```bash + aws s3api put-object-lock-configuration \ + --bucket my-locked-bucket \ + --object-lock-configuration '{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"GOVERNANCE","Days":30}}}' \ + --endpoint-url https://cellar-c2.services.clever-cloud.com + + aws s3api get-object-lock-configuration \ + --bucket my-locked-bucket \ + --endpoint-url https://cellar-c2.services.clever-cloud.com + ``` + + See the AWS CLI documentation for [`put-object-lock-configuration`](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object-lock-configuration.html). + + {{< /tab >}} + +{{< /tabs >}} + +Object Lock provides two retention modes: + +- **Governance** prevents deletion or retention changes unless a client with the required permission explicitly bypasses the protection. Use it when authorized operators must retain an override capability. +- **Compliance** can't be bypassed through the S3 API and prevents deletion or retention-period reductions before expiry. Use it only after validating the policy and duration. + +The default rule applies to object versions uploaded after you configure it. To protect an existing version, set retention directly on that version. Overwriting an object creates another version, while deleting it without a version ID can add a delete marker. The protected version remains stored and recoverable. + +### Hold an object without an expiration date + +A legal hold protects one object version until an authorized user explicitly removes the hold. It is independent of any fixed retention period. The following examples enable and disable a legal hold on the current version of `report.pdf`. + +{{< tabs >}} + + {{< tab name="s3cmd" icon="terminal" >}} + + ```bash + s3cmd setobjectlegalhold ON s3://my-locked-bucket/report.pdf + s3cmd setobjectlegalhold OFF s3://my-locked-bucket/report.pdf + ``` + + {{< /tab >}} + + {{< tab name="MinIO" icon="minio" >}} + + ```bash + mc legalhold set my-cellar/my-locked-bucket/report.pdf + mc legalhold info my-cellar/my-locked-bucket/report.pdf + mc legalhold clear my-cellar/my-locked-bucket/report.pdf + ``` + + Use `--version-id` to target a specific version. See the [`mc legalhold` documentation](https://docs.min.io/community/minio-object-store/reference/minio-mc/mc-legalhold.html) for recursive and version-specific operations. + + {{< /tab >}} + + {{< tab name="AWS CLI" icon="aws" >}} + + ```bash + aws s3api put-object-legal-hold \ + --bucket my-locked-bucket \ + --key report.pdf \ + --legal-hold Status=ON \ + --endpoint-url https://cellar-c2.services.clever-cloud.com + + aws s3api put-object-legal-hold \ + --bucket my-locked-bucket \ + --key report.pdf \ + --legal-hold Status=OFF \ + --endpoint-url https://cellar-c2.services.clever-cloud.com + ``` + + See the AWS CLI documentation for [`put-object-legal-hold`](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object-legal-hold.html). + + {{< /tab >}} + +{{< /tabs >}} + ## Uploading objects with presigned URLs and checksum validation Pre-signed URLs allow the client to upload files directly to S3 without ever exposing your credentials. You simply generate a temporary, time-limited URL and the client uses it to upload their file to your cellar. From 5ce22ad217289ee51a022f98de37bee3a9f2b144 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 26 Aug 2026 22:28:26 +0200 Subject: [PATCH 144/214] guides: add Maudit --- .../styles/config/vocabularies/Doc/accept.txt | 1 + content/guides/_index.md | 1 + content/guides/maudit.md | 92 +++++++++++++++++++ data/icons.yaml | 1 + shared/static-guides.md | 1 + 5 files changed, 96 insertions(+) create mode 100644 content/guides/maudit.md diff --git a/.github/styles/config/vocabularies/Doc/accept.txt b/.github/styles/config/vocabularies/Doc/accept.txt index 0d0a918c5..793b02ac8 100644 --- a/.github/styles/config/vocabularies/Doc/accept.txt +++ b/.github/styles/config/vocabularies/Doc/accept.txt @@ -62,6 +62,7 @@ LTS Lume Materia Matomo +Maudit Maven Metabase middleware diff --git a/content/guides/_index.md b/content/guides/_index.md index 3a9c7df5f..63fd1b931 100644 --- a/content/guides/_index.md +++ b/content/guides/_index.md @@ -44,6 +44,7 @@ llmsTxtOptional: true {{< card link="/developers/doc/kubernetes/operator" title="Kubernetes Operator" subtitle="Manage Clever Cloud add-ons from Kubernetes" icon="kubernetes" >}} {{< card link="/developers/guides/tutorial-laravel" title="Laravel" subtitle="Deploy a Laravel application" icon="laravel" >}} {{< card link="/developers/guides/lume-deno" title="Lume (Deno)" subtitle="Deploy a static Lume website with Deno" icon="deno" >}} + {{< card link="/developers/guides/maudit" title="Maudit" subtitle="Deploy a static Maudit website with Rust" icon="maudit" >}} {{< card link="/developers/guides/mdbook" title="mdBook" subtitle="Deploy a static mdBook website" icon="mdbook" >}} {{< card link="/developers/guides/mkdocs" title="MkDocs" subtitle="Deploy a static MkDocs website" icon="docs" >}} {{< card link="/developers/guides/moodle" title="Moodle" subtitle="Deploy and configure a Moodle learning platform" icon="moodle" >}} diff --git a/content/guides/maudit.md b/content/guides/maudit.md new file mode 100644 index 000000000..a1716dfd2 --- /dev/null +++ b/content/guides/maudit.md @@ -0,0 +1,92 @@ +--- +type: docs +linkTitle: Maudit +title: Deploy a Maudit website +description: Deploy a Maudit static website built with Rust on Clever Cloud +keywords: +- maudit +- rust +- static site generator +- static website +- website deployment +--- + +{{< hextra/hero-subtitle >}} + Deploy a static website built with Maudit and Rust on Clever Cloud. +{{< /hextra/hero-subtitle >}} + +[Maudit](https://maudit.org/) is an open source Rust library for generating static websites. A Maudit project is a regular Rust program that generates its pages and assets during the build phase. + +## Create a Maudit project + +You need [Git](https://git-scm.com/downloads) and [Rust](https://www.rust-lang.org/tools/install) to follow this guide. Install the Maudit CLI and start its interactive project generator: + +```bash +cargo install maudit-cli +maudit init +``` + +Enter `myMauditSite` as the project directory, select the `Basics` template and decline the Git repository initialization. Then move into the generated project: + +```bash +cd myMauditSite +``` + +If you already have a Maudit project, run the following commands from its root directory. + +## Create a Static application + +Install [Clever Tools](/developers/doc/cli/), log in and create a Static application linked to your project folder: + +```bash +npm i -g clever-tools +clever login + +clever create -t static + +# Or link an existing Clever Cloud application: +clever link your_app_name_or_ID +``` + +Clever Tools targets your personal organisation by default. To use another organisation, add `--org ORGANISATION` or `-o ORGANISATION` when you create or link the application. + +## Configure the application + +Use a dedicated S build instance, then set the generated website directory and the command Clever Cloud runs during the build phase: + +```bash +clever scale --build-flavor S +clever env set CC_WEBROOT "/dist" +clever env set CC_BUILD_COMMAND "cargo run --jobs 1 --config profile.dev.debug=0" +``` + +Some Maudit dependencies require significant memory to compile. On an S build instance, a plain `cargo run` can compile several dependencies in parallel and exhaust the available memory. The `--jobs 1` option compiles them sequentially, while `--config profile.dev.debug=0` omits debug information that is unnecessary for generating a static website. + +These options allow the build to use the smallest tested instance size. The command then runs the Maudit project to generate the website in `dist/`, whose contents are served by the Static runtime from its default XS instance. + +## Deploy the website + +Commit the project and deploy it: + +```bash +git init +git add . +git commit -m "First deploy" + +clever deploy +clever open +``` + +You can display your website's URL or add a custom domain. A custom domain also requires [DNS configuration](/developers/doc/administrate/domain-names/): + +```bash +clever domain +clever domain add your.website.tld +``` + +## Learn more + +{{< cards >}} + {{< card link="/developers/doc/applications/static/" title="Static applications" subtitle="Configure and deploy static applications" icon="static" >}} + {{< card link="" title="Maudit documentation" subtitle="Build static websites with Maudit and Rust" icon="maudit" >}} +{{< /cards >}} diff --git a/data/icons.yaml b/data/icons.yaml index 308d742eb..a11779ed2 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -125,6 +125,7 @@ docusaurus: hugo: html: +maudit: mdbook: nuxt: diff --git a/shared/static-guides.md b/shared/static-guides.md index 97e1ce716..b5f1a0c89 100644 --- a/shared/static-guides.md +++ b/shared/static-guides.md @@ -5,6 +5,7 @@ {{< card link="/developers/guides/hexo" title="Hexo" subtitle= "Build and deploy a static Hexo based website on Clever Cloud" icon="hexo" >}} {{< card link="/developers/guides/hugo" title="Hugo" subtitle= "Build and deploy a static Hugo based website on Clever Cloud" icon="hugo" >}} {{< card link="/developers/guides/lume-deno" title="Lume (Deno)" subtitle= "Build and deploy a static Lume (Deno) based website on Clever Cloud" icon="deno" >}} + {{< card link="/developers/guides/maudit" title="Maudit" subtitle= "Build and deploy a static Maudit website with Rust on Clever Cloud" icon="maudit" >}} {{< card link="/developers/guides/mdbook" title="mdBook" subtitle= "Build and deploy a static mbBook based website on Clever Cloud" icon="mdbook" >}} {{< card link="/developers/guides/mkdocs" title="MkDocs" subtitle= "Build and deploy a static MkDocs based website on Clever Cloud" icon="docs" >}} {{< card link="/developers/guides/nuxt" title="Nuxt" subtitle= "Build and deploy a Nuxt application on Clever Cloud" icon="nuxt" >}} From 39a836ed78f6b6f69e77b737bb091bfe86c1acaf Mon Sep 17 00:00:00 2001 From: David Legrand Date: Thu, 27 Aug 2026 02:07:49 +0200 Subject: [PATCH 145/214] addons(metabase): document the official CLI --- content/doc/addons/metabase.md | 37 +++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/content/doc/addons/metabase.md b/content/doc/addons/metabase.md index 47a528d76..63a01aeba 100644 --- a/content/doc/addons/metabase.md +++ b/content/doc/addons/metabase.md @@ -55,7 +55,7 @@ Real ID: metabase_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Name: myMetabase Your Metabase is starting: - - Access it: https://xxxxxxxxxxxxxxxxxxxx-metabase.services.clever-cloud.com + - Access it: https://your-metabase.services.clever-cloud.com - Manage it: https://console.clever-cloud.com/addon_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ``` @@ -75,6 +75,41 @@ Once you created your add-on, open the management URL or look for `MB_SITE_URL` - [Learn how to use Metabase](https://www.metabase.com/learn/metabase-basics/getting-started/find-data) +## Manage content with the Metabase CLI + +The official Metabase CLI (`mb`) interacts with your instance through its API to manage questions, dashboards, collections, transforms, and other content. It differs from the `clever metabase` commands, which manage the Clever Cloud add-on, its version, and lifecycle. + +The CLI requires Metabase 0.58 or later and Node.js 20.6 or later. Install the [official npm package](https://www.npmjs.com/package/@metabase/cli) globally: + +```bash +npm i -g @metabase/cli +``` + +Authenticate with the URL displayed when you create the add-on or available from its [Clever Cloud Console](https://console.clever-cloud.com/) dashboard: + +```bash +mb auth login --url https://your-metabase.services.clever-cloud.com +``` + +On Metabase 0.63 and later, select the recommended browser authentication to sign in with your password or single sign-on (SSO) and approve access. Metabase versions 0.58 to 0.62 require an [API key](https://www.metabase.com/docs/latest/people-and-groups/api-keys), as do automated environments. API keys inherit the permissions of their Metabase group, so select one with only the access required for your tasks. + +Once authenticated, you can check the active profile and manage your instance: + +```bash +mb auth status +mb collection list +mb dashboard list +``` + +Credentials are stored in a local profile. Name profiles when you manage multiple instances: + +```bash +mb auth login --profile production --url https://your-instance-metabase.services.clever-cloud.com +mb dashboard list --profile production +``` + +Some command groups require a newer Metabase version or a Pro or Enterprise feature. The CLI checks these requirements before sending a request. Run `mb --help` to list available commands and refer to the [Metabase CLI documentation](https://www.metabase.com/docs/latest/installation-and-operation/metabase-cli) for complete usage details. + ## Password reset > [!WARNING] From fff643020dd191b94dec45a2d9703329237a9acd Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Fri, 28 Aug 2026 11:30:33 +0200 Subject: [PATCH 146/214] develop(build-hooks): fix Pre Build hook behavior on deployments from cache --- content/doc/develop/build-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/doc/develop/build-hooks.md b/content/doc/develop/build-hooks.md index 099613657..ee06d6567 100644 --- a/content/doc/develop/build-hooks.md +++ b/content/doc/develop/build-hooks.md @@ -77,7 +77,7 @@ If you need support for other hooks, please reach out to our support and explain This hook is ran before the dependencies are fetched. If it fails, the deployment fails. -This hook is ran every time. +This hook is not ran during deployments from cache. This hook is perfect for: From 643bc2866dace0b4f3cec27dc8680da440b56469 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 28 Aug 2026 23:31:23 +0200 Subject: [PATCH 147/214] develop(build-hooks): use active voice for hook execution --- content/doc/develop/build-hooks.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/doc/develop/build-hooks.md b/content/doc/develop/build-hooks.md index ee06d6567..f0038c9e1 100644 --- a/content/doc/develop/build-hooks.md +++ b/content/doc/develop/build-hooks.md @@ -74,10 +74,10 @@ If you need support for other hooks, please reach out to our support and explain **By using `CC_PRE_BUILD_HOOK`**. -This hook is ran before the dependencies are fetched. If it fails, the +This hook runs before the dependencies are fetched. If it fails, the deployment fails. -This hook is not ran during deployments from cache. +This hook doesn't run during deployments from cache. This hook is perfect for: @@ -88,10 +88,10 @@ This hook is perfect for: **By using `CC_POST_BUILD_HOOK`.** -This hook is ran after the project is built, and before the cache archive is +This hook runs after the project is built, and before the cache archive is generated. If it fails, the deployment fails. -This hook is not ran during deployments from cache. +This hook doesn't run during deployments from cache. This hook is perfect for: @@ -104,10 +104,10 @@ This hook is perfect for: **By using `CC_PRE_RUN_HOOK`.** -This hook is ran before the application is started, but after the cache archive +This hook runs before the application is started, but after the cache archive has been generated. If it fails, the deployment fails. -This hook is ran every time. +This hook runs every time. This hook is perfect for: @@ -117,10 +117,10 @@ This hook is perfect for: **By using `CC_RUN_SUCCEEDED_HOOK` or `CC_RUN_FAILED_HOOK`.** -These hooks are ran once the application has started (or has failed starting). +These hooks run once the application has started (or has failed starting). Their failure doesn't cause the deployment to fail. -One of these hooks is ran every time. +One of these hooks runs every time. These hooks are perfect for: From 60b5d4b39f139f84c9d697959b48fa2f2a8954b6 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Mon, 31 Aug 2026 20:26:12 +0200 Subject: [PATCH 148/214] changelog: Keycloak 26.7.3 --- .../changelog/2026/08-31-keycloak-26.7.3.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 content/changelog/2026/08-31-keycloak-26.7.3.md diff --git a/content/changelog/2026/08-31-keycloak-26.7.3.md b/content/changelog/2026/08-31-keycloak-26.7.3.md new file mode 100644 index 000000000..766ed3260 --- /dev/null +++ b/content/changelog/2026/08-31-keycloak-26.7.3.md @@ -0,0 +1,39 @@ +--- +title: Keycloak 26.7.3 (security update) +description: Keycloak 26.7.3 fixes twenty vulnerabilities affecting token exchange, OIDC and fine-grained admin permissions, and corrects performance regressions introduced in 26.7.1 +date: 2026-08-31 +tags: + - addons + - keycloak +authors: + - name: Sébastien Allemand + link: https://github.com/allemas + image: https://github.com/allemas.png?size=40 + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[The release 26.7.3](https://github.com/keycloak/keycloak/releases/tag/26.7.3) of Keycloak is available on Clever Cloud. It's a security update that addresses twenty vulnerabilities, without any critical one, unlike [26.7.2](/changelog/2026/08-24-keycloak-26.7.2/) published last week. + +Two issues stand out. [CVE-2026-18215](https://nvd.nist.gov/vuln/detail/CVE-2026-18215) and [CVE-2026-18214](https://nvd.nist.gov/vuln/detail/CVE-2026-18214), both rated 8.1, let external token exchanges bypass the configured Microsoft tenant and Google hosted domain. [CVE-2026-18571](https://nvd.nist.gov/vuln/detail/CVE-2026-18571), rated 7.2, allows a delegated administrator to assign groups they don't manage when creating a user. + +[CVE-2026-35563](https://nvd.nist.gov/vuln/detail/CVE-2026-35563) carries the highest score of the release, 8.5, but it doesn't affect running instances. The vulnerable Apache Directory client is a test dependency, used by the embedded LDAP server of the Keycloak test suite. The upgrade to ApacheDS 2.0.0.AM27 that closes it leaves the LDAP user federation code untouched. + +The other fixes address medium and low severity issues, mostly in fine-grained admin permissions (FGAP v2) and OIDC edge cases: [CVE-2026-16093](https://nvd.nist.gov/vuln/detail/CVE-2026-16093), [CVE-2026-16072](https://nvd.nist.gov/vuln/detail/CVE-2026-16072), [CVE-2026-16089](https://nvd.nist.gov/vuln/detail/CVE-2026-16089), [CVE-2026-16104](https://nvd.nist.gov/vuln/detail/CVE-2026-16104), [CVE-2026-16105](https://nvd.nist.gov/vuln/detail/CVE-2026-16105), [CVE-2026-16106](https://nvd.nist.gov/vuln/detail/CVE-2026-16106), [CVE-2026-16108](https://nvd.nist.gov/vuln/detail/CVE-2026-16108), [CVE-2026-17059](https://nvd.nist.gov/vuln/detail/CVE-2026-17059), [CVE-2026-18201](https://nvd.nist.gov/vuln/detail/CVE-2026-18201), [CVE-2026-18209](https://nvd.nist.gov/vuln/detail/CVE-2026-18209), [CVE-2026-18218](https://nvd.nist.gov/vuln/detail/CVE-2026-18218), [CVE-2026-18570](https://nvd.nist.gov/vuln/detail/CVE-2026-18570), [CVE-2026-18572](https://nvd.nist.gov/vuln/detail/CVE-2026-18572), [CVE-2026-18573](https://nvd.nist.gov/vuln/detail/CVE-2026-18573), [CVE-2026-79652](https://nvd.nist.gov/vuln/detail/CVE-2026-79652) and [CVE-2026-19729](https://github.com/keycloak/keycloak/issues/51745), an incomplete fix for a path traversal reported in 26.6.4. + +The release also corrects performance issues of the 26.7 branch. Admin API requests no longer resolve every role across all realms, their cost stops growing super-linearly with the number of realms, and an upgrade no longer causes sustained high CPU usage on every node. It fixes an Admin UI regression introduced in 26.7.2, a `NullPointerException` on composite roles referencing a deleted role and offline client session errors. + +Update through the add-on's dashboard in the [Clever Cloud Console](https://console.clever-cloud.com). You can also set `CC_KEYCLOAK_VERSION` of the underlying Java application to `26.7.3` and rebuild it, or use [Clever Tools](/doc/cli/operators/): + +```bash +clever features enable operators + +clever keycloak version check yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId +clever keycloak version update yourKeycloakNameOrId --target 26.7.3 +``` + +- [Read the Keycloak 26.7.3 release notes](https://www.keycloak.org/2026/08/keycloak-2673-released) +- [Learn more about Keycloak on Clever Cloud](/doc/addons/keycloak) From dbc14b5ebb5722ce611ded2a75cc5024b2039b0e Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 1 Sep 2026 14:48:36 +0200 Subject: [PATCH 149/214] postmortem: 2026-08-07 --- content/postmortem/2026-08-27.md | 75 ++++++++++++++++++++++++++++++++ content/postmortem/_index.md | 1 + 2 files changed, 76 insertions(+) create mode 100644 content/postmortem/2026-08-27.md diff --git a/content/postmortem/2026-08-27.md b/content/postmortem/2026-08-27.md new file mode 100644 index 000000000..c7dd0b269 --- /dev/null +++ b/content/postmortem/2026-08-27.md @@ -0,0 +1,75 @@ +--- +title: '2026-08-27' +description: Malformed domain name crash-looping the Sōzu public load balancers, causing a platform-wide outage in every region +date: 2026-08-31 +excludeSearch: true +type: docs +--- + +{{< hextra/hero-subtitle >}} + +A malformed domain name propagated to the public load balancers crash-looped them, making applications, the Console and the APIs unreachable for around 20 minutes. + +{{< /hextra/hero-subtitle >}} + +On August 27, 2026, between 15:55 and 16:15 CEST, the public load balancers of Clever Cloud stopped serving traffic in every public region. Applications hosted on the platform, the Console, and our APIs were unreachable for up to 19 minutes, with traffic progressively recovering from 16:06. No data was lost, and applications themselves kept running throughout: the outage was confined to the routing layer. + +The incident was limited to the public load balancers, which run Sōzu, in every region. Dedicated load balancers weren't affected. Status updates were communicated through: + +## Timeline (CEST) + +| Time | Description | +| ---------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| 2026-08-27 15:55 | Malformed routing update propagated; load balancer workers crash-loop in all public regions; traffic stops being served | +| 2026-08-27 15:55–15:57 | External probes alert the on-call teams | +| 2026-08-27 16:00 | Incident published on the status page | +| 2026-08-27 16:05 | Load balancers fully restarted in the Paris region | +| 2026-08-27 16:06 | Traffic recovers in Paris | +| 2026-08-27 16:09–16:14 | Remaining public regions restarted | +| 2026-08-27 16:15 | All public regions nominal, incident resolved | +| 2026-08-27 16:35 | Root cause reproduced locally; fixes opened the same day | + +## Analysis + +At 15:55 CEST, a call to our self-service API added a domain name to an application, which triggered the propagation of the corresponding ACLs to our load balancers. The update contained an application hostname with a trailing slash (of the form `example.com/`): the faulty control-plane component published the domain name as-is, without stripping the path portion, when adding it to the load balancer configuration. + +Sōzu, the open-source load balancer we develop and operate, interprets a `/` in a hostname as the opening of a regular-expression pattern. Inserting this malformed entry into its routing table failed an internal assertion, which crashed the worker processes. + +Sōzu's main process automatically restarts crashed workers and replays the current routing state to them. Since that state still contained the malformed entry, every new worker crashed within milliseconds, and the load balancers never re-bound their listening ports. Because routing configuration is propagated to all public regions, every public load balancer was affected within seconds. From the outside, connections were accepted by the layer in front and then failed to reach a backend, which surfaced as connection errors and 525 errors on sites behind Cloudflare. + +### Root cause + +Two defects combined: + +- **A control-plane defect**: a component published an application's domain name including its path (`example.com/`) as the load balancer frontend hostname, instead of splitting host and path first +- **A robustness defect in Sōzu**: a malformed frontend hostname made the routing table insertion panic the worker instead of being rejected as an invalid configuration change + +Two factors turned a bad configuration entry into a global outage: + +- Routing configuration is propagated to all public regions at once, with no canary stage +- The worker auto-restart mechanism replays the faulty state, turning a single crash into a persistent crash loop + +### Detection and response + +Our external end-to-end probes fired within seconds and paged the on-call teams at 15:55. Our process-level monitoring of the load balancers reported them as healthy: the main process was alive, only its workers were crash-looping. + +The incident was published on our status page at 16:00. At around 16:05 we fully restarted the load balancer services in the Paris region. A full restart fetches a complete routing configuration from our API, which publishes correctly formed hostnames, so the restarted load balancers came back clean. Traffic in Paris recovered from 16:06, the remaining regions were restarted between 16:09 and 16:14, and all regions were nominal at 16:15. By 16:35 the root cause had been identified and reproduced locally, and fixes were opened the same day. + +### Actions + +**Done:** + +- The faulty control-plane component was taken out of production, fixed and redeployed. It now strips the path from domain names before publishing them to the load balancers +- Our load balancer monitoring now checks the actual listening ports, so a main process with dead workers can no longer report as healthy + +**In progress:** + +- Update Sōzu to reject a malformed frontend hostname as an invalid configuration change instead of panicking: [sozu-proxy/sozu#1312](https://github.com/sozu-proxy/sozu/pull/1312) + +**Longer term:** + +- Per-region routing configuration propagation with a canary region, to reduce the blast radius of faulty updates + +### Conclusion + +A single malformed domain name stopped traffic in every public region for 19 minutes, because two layers that should have contained it did not: the control plane published the entry without validating it, and Sōzu crashed on it instead of rejecting it. We have fixed the component at fault, closed the monitoring blind spot that hid the crash loop, and are hardening Sōzu and our propagation model so that a bad configuration entry stays a local, recoverable event. We apologize to our customers for the disruption. diff --git a/content/postmortem/_index.md b/content/postmortem/_index.md index 20c65c1af..fc2986d62 100644 --- a/content/postmortem/_index.md +++ b/content/postmortem/_index.md @@ -10,3 +10,4 @@ type: docs - [2025-03-03](/postmortem/2025-03-03) - [2025-10-09](/postmortem/2025-10-09) - [2026-03-19](/postmortem/2026-03-19) +- [2026-08-27](/postmortem/2026-08-27) From 457ec857214d7700e480f890703e2f53c7243c77 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 1 Sep 2026 17:00:00 +0200 Subject: [PATCH 150/214] changelog: MySQL 9.7 --- content/changelog/2026/09-01-mysql-9.7.md | 39 +++++++++++++++++++++ content/doc/addons/mysql.md | 2 +- data/software_versions_shared_dedicated.yml | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 content/changelog/2026/09-01-mysql-9.7.md diff --git a/content/changelog/2026/09-01-mysql-9.7.md b/content/changelog/2026/09-01-mysql-9.7.md new file mode 100644 index 000000000..6e6a9fd65 --- /dev/null +++ b/content/changelog/2026/09-01-mysql-9.7.md @@ -0,0 +1,39 @@ +--- +title: MySQL 9.7 is available on Clever Cloud +description: The first MySQL 9 branch supported on Clever Cloud, based on Percona Server 9.7.1-1, the first long term support release since 8.4 +date: 2026-09-01 +tags: + - addons + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +MySQL 9.7 is available on Clever Cloud for add-ons creation and migration, based on [Percona Server for MySQL 9.7.1-1](https://docs.percona.com/percona-server/9.7/release-notes/9.7.1-1.html) released on 5 August 2026. MySQL 8.4 remains the default version for now, so pick 9.7 explicitly from [the Console](https://console.clever-cloud.com) or with Clever Tools: + +```bash +clever addon create mysql-addon myDatabase -p xxs_sml --addon-version 9.7 +``` + +## Why no MySQL 9 branch until now + +MySQL now alternates quarterly innovation releases and long term support (LTS) ones. Each innovation release is only supported until the next one lands, which doesn't match the stability expected from a managed database. Percona, whose server we deploy, [decided not to package any 9.x innovation release](https://www.percona.com/blog/no-mysql-9-x-innovation-releases-from-percona/) and to wait for the next LTS. MySQL 9.7 is that release, and the first one of the branch you can run on Clever Cloud. + +Percona also skipped 9.7.0, [which shipped with higher severity CVEs](https://www.percona.com/blog/inside-mysql-9-7-lts-features/), and started the series at 9.7.1 with those fixes already applied. + +## What MySQL 9.7 brings + +The [Hypergraph Optimizer](https://www.percona.com/blog/inside-mysql-9-7-lts-features/) reaches the community edition and rewrites how complex joins are planned. JSON duality views expose relational data as JSON documents you can read and write through a single format. + +Group replication gets flow control monitoring, extended multi-threaded applier statistics, automatic eviction and rejoin of unhealthy nodes, and an up-to-date aware primary election that picks the most synchronized secondary. A lower-version replica can also connect to a higher-version source, which allows phased upgrades across a replication topology. + +## End of life of previous versions + +MySQL 5.7 and 8.0 are end-of-life, and Percona [published its final 8.0 release in June 2026](https://docs.percona.com/new/2026/06/10/percona-server-for-mysql-8046-37-has-been-released/). Neither branch receives regular security updates anymore. If you still run a 5.7 or 8.0 add-on, migrate it to 8.4 or 9.7 through our migration process. + +- [Learn more about MySQL on Clever Cloud](/doc/addons/mysql/) +- [Learn more about MySQL 9.7 features](https://docs.percona.com/percona-server/9.7/whats-new-mysql-9.html) +- [Percona Server for MySQL 9.7 documentation](https://docs.percona.com/percona-server/9.7/index.html) diff --git a/content/doc/addons/mysql.md b/content/doc/addons/mysql.md index 142ca3d18..95820279b 100644 --- a/content/doc/addons/mysql.md +++ b/content/doc/addons/mysql.md @@ -24,7 +24,7 @@ MySQL is an open source relational database management system (RDBMS). Clever Cl ## Supported Versions -Use the 8.4 long term support (LTS) branch for new add-ons. MySQL 8.0 [reached its end of life on 30 April 2026](https://www.mysql.com/support/eol-notice.html) and [Percona Server 8.0 had its final release in June 2026](https://docs.percona.com/new/2026/06/10/percona-server-for-mysql-8046-37-has-been-released/), so we recommend you migrate your existing 8.0 add-ons to 8.4. +Two long term support (LTS) branches are available for new add-ons: 8.4, used by default, and [9.7](/changelog/2026/09-01-mysql-9.7/). MySQL 5.7 and 8.0 are end-of-life and [Percona Server 8.0 had its final release in June 2026](https://docs.percona.com/new/2026/06/10/percona-server-for-mysql-8046-37-has-been-released/), so we recommend you migrate your existing add-ons on these branches to 8.4 or 9.7. {{< software_versions_shared_dedicated mysql>}} diff --git a/data/software_versions_shared_dedicated.yml b/data/software_versions_shared_dedicated.yml index 2e90e282f..fc8358c7d 100644 --- a/data/software_versions_shared_dedicated.yml +++ b/data/software_versions_shared_dedicated.yml @@ -17,6 +17,7 @@ mysql: - 5.7 (EOL) - 8.0.46 (EOL) - 8.4.10 + - 9.7.1 dev: - 8.0.46 (EOL) From 9259965d733efee08f7f08a1e51c815bfe93c96f Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 1 Sep 2026 17:08:00 +0200 Subject: [PATCH 151/214] changelog: PostgreSQL 18 by default --- .../2026/09-01-postgresql-18-default.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 content/changelog/2026/09-01-postgresql-18-default.md diff --git a/content/changelog/2026/09-01-postgresql-18-default.md b/content/changelog/2026/09-01-postgresql-18-default.md new file mode 100644 index 000000000..ed8d7a2de --- /dev/null +++ b/content/changelog/2026/09-01-postgresql-18-default.md @@ -0,0 +1,30 @@ +--- +title: PostgreSQL 18 becomes the default version on 15 September +description: New PostgreSQL add-ons created without an explicit version will run PostgreSQL 18 +date: 2026-09-01 +tags: + - addons + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[PostgreSQL 18 is available on Clever Cloud](/changelog/2025/10-08-postgresql-18/) since October 2025, and [PostgreSQL 17 has been the default](/changelog/2025/03-27-postgresql-default/) for new add-ons since March 2025. **Starting 15 September 2026**, a new PostgreSQL add-on created without an explicit version runs PostgreSQL 18. + +This affects add-ons creation only. Existing databases keep the version they run today and are never upgraded without your action. When you're ready, use Clever Cloud's automatic migration process to move them to PostgreSQL 18. + +You can still create an add-on on any other supported version, from the Console or with Clever Tools: + +```bash +clever addon create postgresql-addon myDatabase -p xxs_sml --addon-version 17 +``` + +PostgreSQL 18 uses the same default extensions as PostgreSQL 17. It brings asynchronous I/O with `io_uring`, [virtual generated columns](https://www.postgresql.org/docs/18/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-STORED), UUIDv7 generation, improved text processing and multiple authentication and security features. + +Keep in mind that PostgreSQL 14 reaches its end of life on 12 November 2026. Plan a migration to a newer version before that date to keep receiving security updates. + +- [Learn more about PostgreSQL on Clever Cloud](/doc/addons/postgresql/) +- [PostgreSQL versioning policy](https://www.postgresql.org/support/versioning/) From fb51560b5a558efe309d64f8562db5cfb6ae650e Mon Sep 17 00:00:00 2001 From: David Legrand Date: Tue, 1 Sep 2026 17:15:00 +0200 Subject: [PATCH 152/214] changelog: PHP 8.5 by default --- .../changelog/2026/09-01-php-8.5-default.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 content/changelog/2026/09-01-php-8.5-default.md diff --git a/content/changelog/2026/09-01-php-8.5-default.md b/content/changelog/2026/09-01-php-8.5-default.md new file mode 100644 index 000000000..869456c2c --- /dev/null +++ b/content/changelog/2026/09-01-php-8.5-default.md @@ -0,0 +1,39 @@ +--- +title: PHP 8.5 becomes the default version in October +description: Set CC_PHP_VERSION to 8.4 before the October image release if you want to keep your current PHP version +date: 2026-09-01 +tags: + - images + - update +authors: + - name: David Legrand + link: https://github.com/davlgd + image: https://github.com/davlgd.png?size=40 +excludeSearch: true +--- + +[PHP 8.5 is available](/changelog/2026/01-15-images-update/) on Clever Cloud since January 2026, and [on the FrankenPHP runtime](/changelog/2026/02-25-images-update/) since February 2026, through the `CC_PHP_VERSION=8.5` environment variable. PHP 8.4 is still the default version for both runtimes. + +In the image release that follows **1 October 2026**, PHP 8.5 becomes that default. From then on, a PHP or FrankenPHP application deployed without `CC_PHP_VERSION`, or with `CC_PHP_VERSION=8`, runs PHP 8.5. + +## Keep your current version + +If you want to stay on PHP 8.4 or an earlier version, set `CC_PHP_VERSION` explicitly **before that date**, from the Console or with Clever Tools: + +```bash +clever env set CC_PHP_VERSION 8.4 +``` + +Applications that already pin a version, such as `CC_PHP_VERSION=8.3`, are not affected. + +## Prepare the move + +Deploy a review app or a test application with `CC_PHP_VERSION=8.5` to validate your code before October. Check that the extensions you rely on are [available for PHP 8.5](/doc/applications/php/extensions/), as a few of them aren't packaged for this version yet. + +PHP 8.4 [leaves active support on 31 December 2026](https://www.php.net/supported-versions.php), but keeps receiving security fixes until 31 December 2028. Pinning `CC_PHP_VERSION=8.4` is a supported choice for the next two years, not a temporary workaround. PHP 8.5 gets active support until the end of 2027 and security fixes until the end of 2029, so moving now gives you a full extra year of upstream bug fixes. + +Take this opportunity to check your older applications: PHP 8.2 reaches its end of life on 31 December 2026 and stops receiving security fixes then. If you run `CC_PHP_VERSION=8.2`, plan an upgrade before the end of the year. + +- [Learn more about PHP on Clever Cloud](/doc/applications/php/) +- [Learn more about FrankenPHP on Clever Cloud](/doc/applications/frankenphp/) +- [PHP migration guide from 8.4 to 8.5](https://www.php.net/migration85) From 9dca32ed85fafba14f1dfe763e3612a3043fa913 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 07:27:31 +0200 Subject: [PATCH 153/214] docs(contributing): align commit conventions with changelog practice --- AGENTS.md | 9 +++++++-- CONTRIBUTING.md | 12 +++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1a2b8ee7b..8ddc0021f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -109,15 +109,20 @@ For changelog entries, also include: - For content updates, use `section(page): commit message`, for example: - `addons(postgresql): document pg_partman support` - `applications(nodejs): clarify pnpm configuration` - - `changelog(metabase): announce 0.63.14 security update` - `guides: add SvelteKit` for a new deployment guide +- For changelog entries, use `changelog: what you announce`. Name the product and its version, or the change itself, instead of starting with a verb: + - `changelog: Keycloak 26.7.3` + - `changelog: MySQL 8.0.46 and 8.4.10` + - `changelog: PostgreSQL 18 by default` + - `changelog: images updates, 2026W34` +- Commit a changelog entry with the documentation pages and data files it relies on, so an announcement never lands before the pages it links to - For documentation structure, Hugo, deployment, CI, tooling, or dependency changes, use standard Conventional Commits, for example: - `feat(hugo): add a shortcode for version tables` - `fix(ci): run Vale on shared content` - `refactor(layouts): simplify changelog rendering` - `chore(deps): update the Hextra theme` - Split content and structural changes into separate commits when possible -- Start the subject with a lowercase imperative verb +- Start the subject with a lowercase imperative verb, except for changelog entries ### Code and Technical Examples diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0fd3b86e..30e56fa97 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,12 +107,22 @@ For content updates, use `section(page): commit message`. The section and page i ```text addons(postgresql): document pg_partman support applications(nodejs): clarify pnpm configuration -changelog(metabase): announce 0.63.14 security update guides: add SvelteKit ``` Use `guides: add Product` when adding a deployment guide. Start the subject with a lowercase imperative verb. +Changelog entries are the exception. Use `changelog: what you announce`, naming the product and its version or the change itself rather than starting with a verb: + +```text +changelog: Keycloak 26.7.3 +changelog: MySQL 8.0.46 and 8.4.10 +changelog: PostgreSQL 18 by default +changelog: images updates, 2026W34 +``` + +Commit a changelog entry together with the documentation pages and data files it relies on, so an announcement never lands before the pages it links to. + For changes to the documentation structure, Hugo configuration or templates, deployment, CI, tooling, or dependencies, use the standard Conventional Commits format `type(scope): commit message`: ```text From 65145e584ae540fde351fb75d67beee2c1ddda94 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 07:49:07 +0200 Subject: [PATCH 154/214] chore: remove dead files, drafts and stale lint configuration --- .github/styles/Guides/ellipsis.yml | 10 -- .gitignore | 1 - .gitlab-ci.yml | 7 - .markdownlintignore | 2 - .vale.ini | 4 +- README.md | 2 +- content/_index.md | 1 - drafts/community.md | 24 ---- drafts/elastic.md | 16 --- drafts/medusajs.md | 46 ------- drafts/mysql.md | 12 -- drafts/notifications.md | 151 ---------------------- drafts/postgresql.md | 20 --- drafts/pricing.md | 12 -- drafts/shortcodes.md | 87 ------------- drafts/ssh-access.md | 83 ------------ layouts/docs/single.html | 19 --- layouts/partials/components/feedback.html | 42 ------ layouts/shortcodes/imgproc.html | 60 --------- shared/git-push.md | 21 --- static/cloud-synccloud-icon.png | Bin 3736 -> 0 bytes static/favicon.ico.png | Bin 1480 -> 0 bytes static/images/open_the_app_ico.png | Bin 663 -> 0 bytes static/images/pg-studio.png | Bin 741685 -> 0 bytes 24 files changed, 3 insertions(+), 617 deletions(-) delete mode 100644 .github/styles/Guides/ellipsis.yml delete mode 100644 .gitlab-ci.yml delete mode 100644 .markdownlintignore delete mode 100644 drafts/community.md delete mode 100644 drafts/elastic.md delete mode 100644 drafts/medusajs.md delete mode 100644 drafts/mysql.md delete mode 100644 drafts/notifications.md delete mode 100644 drafts/postgresql.md delete mode 100644 drafts/pricing.md delete mode 100644 drafts/shortcodes.md delete mode 100644 drafts/ssh-access.md delete mode 100644 layouts/docs/single.html delete mode 100644 layouts/partials/components/feedback.html delete mode 100644 layouts/shortcodes/imgproc.html delete mode 100644 shared/git-push.md delete mode 100644 static/cloud-synccloud-icon.png delete mode 100644 static/favicon.ico.png delete mode 100644 static/images/open_the_app_ico.png delete mode 100644 static/images/pg-studio.png diff --git a/.github/styles/Guides/ellipsis.yml b/.github/styles/Guides/ellipsis.yml deleted file mode 100644 index 305d64fb4..000000000 --- a/.github/styles/Guides/ellipsis.yml +++ /dev/null @@ -1,10 +0,0 @@ -extends: existence -message: "Include the framework in the title!" -nonword: true -level: error -scope: heading -action: - name: remove -tokens: - - '\[\.\.\.\]' - diff --git a/.gitignore b/.gitignore index 9463fbde0..e2bd9db11 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ Thumbs.db # Configuration files .clever.json -.vale.ini # Build/cache files/folders /.hugo_build.lock diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index c2c88abe7..000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,7 +0,0 @@ -include: - - component: $CI_SERVER_HOST/pipelines/clever-cloud-pipeline/deploy-to-prod@~latest - inputs: - stage: deploy - when: always - - diff --git a/.markdownlintignore b/.markdownlintignore deleted file mode 100644 index a6cb7ebc9..000000000 --- a/.markdownlintignore +++ /dev/null @@ -1,2 +0,0 @@ -/public/** -/resources diff --git a/.vale.ini b/.vale.ini index ebc876a2e..b39f99934 100644 --- a/.vale.ini +++ b/.vale.ini @@ -7,11 +7,11 @@ MinAlertLevel = suggestion Packages = Hugo, Google [*.md] -BasedOnStyles = Clever, Vale, Google, Guides +BasedOnStyles = Clever, Vale, Google Google.Headings = NO Vale.Terms = NO Google.Parens = NO Google.Ellipses = NO [*.xml] -Transform = docbook-xsl-snapshot/html/docbook.xsl \ No newline at end of file +Transform = docbook-xsl-snapshot/html/docbook.xsl diff --git a/README.md b/README.md index 1f5f26cf5..39afbe9a4 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ Run a production build before submitting a change: hugo ``` -The repository provides [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) rules in [`.markdownlint.jsonc`](./.markdownlint.jsonc), CLI configuration in [`.markdownlint-cli2.jsonc`](./.markdownlint-cli2.jsonc) and editor exclusions in [`.markdownlintignore`](./.markdownlintignore). Run the version installed by Mise: +The repository provides [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) rules in [`.markdownlint.jsonc`](./.markdownlint.jsonc) and CLI configuration in [`.markdownlint-cli2.jsonc`](./.markdownlint-cli2.jsonc), which excludes the paths listed in [`.gitignore`](./.gitignore). Run the version installed by Mise: ```bash markdownlint-cli2 "**/*.md" diff --git a/content/_index.md b/content/_index.md index a3018df56..dae236a18 100644 --- a/content/_index.md +++ b/content/_index.md @@ -1,5 +1,4 @@ --- -type: docs title: Clever Cloud Documentation description: Documentation and guides to deploy, manage, and monitor your services on Clever Cloud layout: hextra-home diff --git a/drafts/community.md b/drafts/community.md deleted file mode 100644 index 661cd1bc7..000000000 --- a/drafts/community.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -type: docs -title: Community -weight: 6 -keywords: -- support -- community -- gitter -- irc -draft: true ---- - -Engage in our Developer Community by seeking or providing advice on application and development level questions. We have a community of developers using our platform and you can help and be helped there. - -## Gitter - -- English room: -- French room: - -## Public IRC Channel - -We’re also available on IRC. You can join our channel: - -- [#clevercloud on irc.freenode.net](irc://irc.freenode.net:6667/clevercloud) diff --git a/drafts/elastic.md b/drafts/elastic.md deleted file mode 100644 index 59edd88a3..000000000 --- a/drafts/elastic.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -type: docs -title: Elastic -description: Elastic guides -keywords: -- elastic -- kibana -- APM -aliases: -- /doc/deploy/addon/elastic/kibana -draft: true ---- -{{< cards >}} - {{< card link="/developers/guides/elastic/kibana" title="Customize Kibana" subtitle= "Deploy and customize a Kibana server on Clever Cloud" icon="kibana" >}} - -{{< /cards >}} diff --git a/drafts/medusajs.md b/drafts/medusajs.md deleted file mode 100644 index eed0a3bbd..000000000 --- a/drafts/medusajs.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: 'Medusa.js' -date: 2023-11-13T11:00:11+01:00 -draft: true -type: docs ---- - -## Create your Medusa.js project from scratch - -1. Deploy a PostgreSQL add-on and a Redis add-on -2. Copy the value of `POSTGRESQL_ADDON_URI` -3. In your terminal, run `npx create-medusa-app@latest --db-url ` -4. Follow the steps to configure your Medusa project -5. In your code, replace `DB_URL` by `POSTGRESQL_ADDON_URI` -6. Create a Node.js app in Clever Cloud -7. Inject your environment variables -8. Set a domain name and inject it as an environment variable: `MEDUSA_URL`=`}} - {{< card link="/developers/guides/proxysql" title="ProxySQL" subtitle= "Configure and use ProxySQL for MySQL add-ons" icon="mysql" >}} -{{< /cards >}} diff --git a/drafts/notifications.md b/drafts/notifications.md deleted file mode 100644 index 10fe9fc8b..000000000 --- a/drafts/notifications.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -type: docs -title: Notifications -description: How to setup and configure notifications via clever-tools -keywords: -- notification -- webhook -- e-mail -- email -- slack -- flowdock -aliases: -- /doc/administrate/clever-tools/notifications -- /doc/cli/notifications/ -draft: true ---- - -The *Notifications* service allows you to choose the events of interest to you -and decide where to send corresponding notifications. - -## E-mails - -Historically and by default, deployment result e-mails (deployment succeeded or -failed) were always sent to the owners of an application, namely the user -themselves or the organisation's members. - -Using the `notify-email` command of *clever-tools*, you can choose to disable -this or to restrict it to only some applications. - -To do this, you need to delete the hook named *Default deployment result -e-mails*, then add a new one and set it to your liking. - -Issue this command to learn more about this: `clever help notify-email`. - -In the future, this service will also handle credits warning notifications and -others to come. You will be able to choose wether or not to receive an e-mail -and/or for these events to be forwarded to a web service via a *webhook*. - -## Webhooks - -Webhooks are defined as follows: - -> A webhook in web development is a method of augmenting or altering the -> behavior of a web page, or web application, with custom callbacks. These -> callbacks may be maintained, modified, and managed by third-party users and -> developers who may not necessarily be affiliated with the originating website -> or application. -> — Wikipedia[^1] - -[^1]: Excerpted from Wikipedia's [webhook page](https://en.wikipedia.org/wiki/Webhook) - -In practical terms, this means that you can choose to receive all deployment -result events on an endpoint of your application without having to listen to -all events via the Websocket API. No more lost events and having to reconnect -to the websocket if and when it fails. - -*clever-tools* has a `webhooks` command which allows you to list existing -webhooks, remove one or add a new one. - -You can issue this command to learn more about it: `clever help webhooks`. - -### Example: Slack deployment results Webhook - -First, you will have to create the *Incoming WebHooks* integration on Slack: - -Let's go to the [custom integrations -page](https://slack.com/apps/manage/custom-integrations), once there go to -*Incoming WebHooks*. - -You will find an *Add Configuration* button on the left which will take you to -the page where you need to configure the integration. You just need to select -the channel where you want the notifications to be posted, then it will show -you the Webhook URL, it starts with `https://hooks.slack.com/services/`. Copy -that URL. - -Now, let's create the Webhook! - -Go into the application directory for which you want to receive notifications -and there issue this command: - -```shell -clever webhooks add "deployment results on Slack" https://hooks.slack.com/services/xxxxxx/yyyyyy/zzzzzzzzzzzzz --format slack --event META_DEPLOYMENT_RESULT -``` - -#### Events available - -The list of values that can be assigned to `--event` option: - -**Account:** - -- `ACCOUNT_CREATION` -- `ACCOUNT_DELETION` -- `ACCOUNT_EDITION` - -**Addon:** - -- `ADDON_CREATION` -- `ADDON_DELETION` - -**Application:** - -- `APPLICATION_CREATION` -- `APPLICATION_DELETION` -- `APPLICATION_EDITION` -- `APPLICATION_REDEPLOY` -- `APPLICATION_STOP` - -**Deployment:** - -- `DEPLOYMENT_ACTION_BEGIN` -- `DEPLOYMENT_ACTION_END` -- `DEPLOYMENT_FAIL` -- `DEPLOYMENT_SUCCESS` -- `GIT_PUSH` - -**Organisation:** - -- `ORGANISATION_CREATION` -- `ORGANISATION_DELETION` -- `ORGANISATION_EDITION` -- `ORGANISATION_USER_ADDITION` - -**Meta:** - -- `META_SERVICE_LIFECYCLE` = APPLICATION_STOP, DEPLOYMENT_ACTION_BEGIN, DEPLOYMENT_FAIL, DEPLOYMENT_SUCCESS -- `META_DEPLOYMENT_RESULT` = DEPLOYMENT_FAIL, DEPLOYMENT_SUCCESS -- `META_SERVICE_MANAGEMENT` = ADDON_CREATION, ADDON_DELETION APPLICATION_CREATION, APPLICATION_EDITION, APPLICATION_DELETION` -- `META_CREDITS` = CREDITS_ADDED - -**Others:** - -- `CLEVER_TOOLS_REQUEST` -- `CREDITS_ADDED` - -##### Examples - -Get a message in a Slack channel when the deployment for an application failed: - -```shell -clever webhooks add "The application failed to deploy" https://hooks.slack.com/services/xxxxxx/ --format slack --event DEPLOYMENT_FAIL -``` - -Notify your accounting service when credits are added: - -```shell -clever webhooks add "credits added" https://hooks.slack.com/services/xxxxxx/yyyyyy/zzzzzzzzzzzzz --format raw --event META_CREDITS -``` - -All done! You can now push your application (`clever deploy`) or restart it -(`clever restart`) and you will see the result of the deployment in your Slack -channel. diff --git a/drafts/postgresql.md b/drafts/postgresql.md deleted file mode 100644 index 14cf692a3..000000000 --- a/drafts/postgresql.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -type: docs -title: PostgreSQL -description: Configure and manage your PostgreSQL add-ons -keywords: -- balancing -- connection -- limit -- load -- pgpool -- pool -- postgres -- proxy -- sql -draft: true ---- - -{{< cards >}} - {{< card link="/developers/guides/pgpool" title="Pgpool-II" subtitle= "How to configure and use Pgpool-II for PostgreSQL add-ons" icon="pg" >}} -{{< /cards >}} diff --git a/drafts/pricing.md b/drafts/pricing.md deleted file mode 100644 index 194ed299a..000000000 --- a/drafts/pricing.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -type: docs -title: Pricing -draft: true -description: Pricing table of Clever Cloud's runtimes -keywords: -- pricing -aliases: - - /clever-cloud-overview/pricing/ ---- - -Our up-to-date prices (as well as a pricing calculator) is available on the [pricing page](https://www.clever.cloud/pricing). diff --git a/drafts/shortcodes.md b/drafts/shortcodes.md deleted file mode 100644 index dd27d6755..000000000 --- a/drafts/shortcodes.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -type: docs -weight: 150 -title: Shortcode -description: Learn about the available shortcodes to write new documentation. -keywords: -- contribution -- shortcode -- writing -draft: true ---- - -To call a shortcode you need to write `{{}}`. - -## Alert shortcode - -Use the `alert` shortcode when you want to outline something in particular. The first parameter is the level of the alert, the second is an optional title. Everything inside the shortcode will be the message. The list of alert level is available on [Bootstrap documentation](https://getbootstrap.com/docs/4.0/components/alerts/). - -Here's an example for a warning alert: - -```go -{{}} -This is the message of the warning. -{{}}. -``` - -This will render: - -{{< callout type="warning" >}} -This is the message of the warning. -{{< /callout >}}. - -## image shortcode - -Use the `image` shortcode if you want to embed an image. The first parameter is the link to the image. The second is the title/caption of the image. - -```go -{{}} -``` - -## readfile shortcode - -Use the `readfile` shortcode to include a partial document in your content. It acts as a templating solution to enable reuse of common content. - -```go -{{}} -``` - -It's possible to search and replace variable during templating. Make sure you have a dictionary called `str_replace_dict` in the frontmatter section of the page. - -```go -str_replace_dict: - "@application-type@": "Go" -``` - -## githubReadme shortcode - -Use the `githubReadme` shortcode to embed the Readme of an existing Github project in a page. The first parameter is the group and name of the repository. - -This shortcode will embed the clever-tools readme. - -```go -{{}} -``` - -## tooltip shortcode - -Use the `tooltip` shortcode to provide a definition of the a term in a tooltip. All terms definitions are fetched from the glossary under `/data/glossary.json`. -the `title` parameter is the identifier of the object from `glossary.json`. - -This tooltip - -```go -{{}}PaaS{{}} -``` - -will produce {{< tooltip title="paas" >}}PaaS{{< /tooltip >}} - -## ref shortcode - -This is a default Hugo shortcode to link to another content page. - -This will output the relative link to the common configuration page: `reference/common-configuration/#private-ssh-key` - -```go -{{}} -``` diff --git a/drafts/ssh-access.md b/drafts/ssh-access.md deleted file mode 100644 index 8f840db48..000000000 --- a/drafts/ssh-access.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -type: docs -title: SSH access -weight: 5 -description: SSH access to running instances -keywords: -- ssh -- shell -- console -aliases: -- /doc/administrate/clever-tools/ssh-access -draft: true ---- - -While direct SSH access to instances is not recommended in an [immutable infrastructure](https://boxfuse.com/blog/no-ssh.html) setup, it can be useful for debugging purposes. Clever Cloud allows you to connect to running instances via SSH. - -{{< callout type="warning" >}} -Clever Cloud instances are to be seen as *read-only* resources. Any change made on an instance **will not be persisted**. You can use SSH access for quick tests, but if you want to persist changes, you need to commit them in your repository. Changes made on instances are not kept across deployments. -{{< /callout >}} - -{{< callout type="info" >}} -SSH on docker instances will attach to the running container. You must have `/bin/bash` installed (usually from the `bash` package) otherwise attaching to the container will fail. -{{< /callout >}} - -## Requirements - -You need to have the Clever Tools CLI installed locally. - -[//]: # (ref "/doc/quickstartclever-tools-intro") - -### Make sure you have a properly configured SSH key - -To use SSH access, you need to have an SSH key properly configured in your Clever Cloud account. Please refer to [the SSH keys section of the documentation](/doc/account/ssh-keys-management) to know how to set up your SSH keys. - -## Accessing your machine with Clever Tools CLI - -In order to access the machine via SSH using the Clever Tools CLI - -[//]: # (ref "/doc/quickstartclever-tools-intro) - -you need to have an application running on Clever Cloud and have linked it with your local repository using the Clever Tools CLI with `clever link --org ` - -### Accessing your machine - -You can access running instances of a linked application with `$ clever ssh` in the linked application's repository locally. - -```shell -$ clever ssh -> Opening an ssh shell -> bas@67fbf787-3518-47bb-abd9-2c2575844edd ~ $ -``` - -If multiple instances are running, you will be asked which one to use: - -```shell -$ clever ssh -> 1) Sleepy Ponita - Instance 0 - UP (11281f38-31ff-43a7-8595-a2d82630c32b) -> 2) Tense Caterpie - Instance 1 - UP (b10d19d9-5238-408b-b038-3e32c7a301c2) -> Your choice: 1 -> Opening an ssh shell -> bas@11281f38-31ff-43a7-8595-a2d82630c32b ~ $ -``` - -You are now connected to the machine. - -### Note for Windows users - -`$ clever ssh` command will fail on PowerShell or cmd.exe if there is no `ssh.exe` in your path. -The most straightforward solution is to start `$ clever ssh` from `git-bash` but you can also add `ssh.exe` in your path.. - -## Access your application's folder - -No matter wich way you've decided to use to SSH to the machine, your application's folder is located at: `/home/bas/`. - -## Show your application's logs - -If you want to show your application's logs while you debug: - -```shell -journalctl -efa -u bas-deploy.service -``` - -You can also use `journalctl` [with other options](https://www.commandlinux.com/man-page/man1/journalctl.1.html) if you need to. diff --git a/layouts/docs/single.html b/layouts/docs/single.html deleted file mode 100644 index 4a60b0c77..000000000 --- a/layouts/docs/single.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ define "main" }} -
- {{ partial "sidebar.html" (dict "context" .) }} - {{ partial "toc.html" . }} -
-
- {{ partial "breadcrumb.html" (dict "page" . "enable" true) }} -
- {{ if .Title }}

{{ .Title }}

{{ end }} - {{ .Content }} -
- {{ partial "components/last-updated.html" . }} - {{ partial "components/feedback.html" . }} - {{ partial "components/pager.html" . }} - {{ partial "components/comments.html" . }} -
-
-
-{{ end }} diff --git a/layouts/partials/components/feedback.html b/layouts/partials/components/feedback.html deleted file mode 100644 index ad10d0030..000000000 --- a/layouts/partials/components/feedback.html +++ /dev/null @@ -1,42 +0,0 @@ -
-

Did this documentation help you ?

-
-

- - -

-

- - - - \ No newline at end of file diff --git a/layouts/shortcodes/imgproc.html b/layouts/shortcodes/imgproc.html deleted file mode 100644 index 37e0e181b..000000000 --- a/layouts/shortcodes/imgproc.html +++ /dev/null @@ -1,60 +0,0 @@ -{{- $context := . -}} -{{- $caption := .Get "caption" -}} -{{- $image := .Get "image" -}} -{{- $width := 0 -}} -{{- $height := 0 -}} -{{- $imageStyle := .Get "imageStyle" -}} - -{{/* Image processing options */}} -{{- $method := .Get "method" | default "Resize" | humanize -}} -{{- $options := .Get "options" | default "900x webp q95" -}} - -{{- if and $image (not (urls.Parse $image).Scheme) -}} - {{/* Process images in assets */}} - {{- with resources.Get $image -}} - {{- $processed := "" -}} - {{- if eq $method "Resize" -}} - {{- $processed = (.Resize $options) -}} - {{- else if eq $method "Fit" -}} - {{- $processed = (.Fit $options) -}} - {{- else if eq $method "Fill" -}} - {{- $processed = (.Fill $options) -}} - {{- else if eq $method "Crop" -}} - {{- $processed = (.Crop $options) -}} - {{- else -}} - {{- errorf "Invalid image processing command: Must be one of Crop, Fit, Fill or Resize." -}} - {{- end -}} - {{- $width = $processed.Width -}} - {{- $height = $processed.Height -}} - {{- $image = $processed.RelPermalink -}} - {{- else -}} - {{/* Otherwise, use relative link of the image */}} - {{- if hasPrefix $image "/" -}} - {{- $image = relURL (strings.TrimPrefix "/" $image) -}} - {{- end -}} - {{- end -}} -{{- end -}} - - - - -{{- $padding := "p-4" -}} -{{- with $caption -}} - {{- $padding = "pt-4 px-4" -}} -{{- end -}} - -{{- with $caption -}} -
{{- $caption | markdownify -}}
-{{- end -}} - -
\ No newline at end of file diff --git a/shared/git-push.md b/shared/git-push.md deleted file mode 100644 index bba67c947..000000000 --- a/shared/git-push.md +++ /dev/null @@ -1,21 +0,0 @@ -## Push your code - -Once you complete these steps, commit your content to the local repository and deploy it: - -```bash -git add . -git commit -m "First deploy" -clever deploy -clever open -``` - -You can display your website's URL or add a custom domain to it (you'll need to configure DNS): - -```bash -clever domain -clever domain add your.website.tld -``` - -## 404 page location - -If you need to use a specific page for 404 errors, define its location with `SERVER_ERROR_PAGE_404` environment variable from Static Web Server, used as default in `static` runtime. For example : `SERVER_ERROR_PAGE_404=404.html`. diff --git a/static/cloud-synccloud-icon.png b/static/cloud-synccloud-icon.png deleted file mode 100644 index eb8f9f65f3ec75cefe6bcf1a6c87929c4e47cce1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3736 zcmV;J4rlR+P)n`pP#k!GX&SY+wv2XNouR4SR#r#6077bt#B-cI@zzY* z1MFc*L}8N(ByBg;ZD?^2TZc5st`KO0q1;ML&Hk{sP}VH={SN1Rf3BUbNt4*u$=d#; z0#uT7pG{V}{jmzH0<@i|$#gFHG0fyB zz%CW26e&uRf|LpfARtnTka(9g5R{COJReX^l8T+JAxtEcSH64dv=41WTZo#>~6P7f0;7IfN!Eko!DW9*r%tm#A-&4_K=~>8^P0*o`QtYde zOi~x~(y6V8%+Sp~TbDU4X>t*JcTAlgffl3XM4j9D-NS(j2F-LbK1T?G`nJd%ov6;x zSod2?`}SYkyfYmDq*1{kCT_a|YkA=Q%#`GrnQt{&EhTDlXD-c$ENPPXibB6*9CeYO z8`$>d3q@!F#y38YVp{r{mpp(z4T?KcdWtgQUiYqde_w_cp|wPPyJwQ;gDm-=8m1TX zc4XV@r-mT{FkyMN)uTM+I<%t)&smuN@y%7qk8%rBV||GJ7N$YP zvQd#FVCOxIamw;(FQES3oyUUIEg#Puh8CeEL`~*$>~QrIKp8BCRKbz2ygt@4^Pdmd zcV8*xB4=Fu99n)NdLKLsZA(N8g5!(aO2$(FA^^WlEBEDxcmPfhy2 zES||Vmp;iu3(yjxVjdoH``Vs3isl1H8US7ifjuYbDBg^PSIB+P0yIq&PdhQ~?QT`D zUuJP@amc8RXKph+}Ml*cKC-6gQT0d9rJSGxHSpIKXW zi=K4@z;WElTNGDi!~=IlSE<&$usSq`DQeEg8A)=GS|3|UFcQR0?e^Ay&j zO|FnYto^CW<=`Bzz(Xs_z86e}!u_S?o(P*tVlx9#nl@0eHgycd4-M1N3I z1=8ZbRwlvf+n}6;8_5^-r--`v<0lSR0~&PI5hLN6A^>wXAu8pj=btaaXBeX!_KJ_{=L<^NxuUB) zO3z8LgIMqW%IF-wHHWC_zI+PV1ev6yuCs|s$tgM0Ua8e+VB@cz8-!~Jqde8P+;LQs zWi86gRIISP-~DK%?z86jsFQF^G2~nMhv4IOHTR zkwcB|4T<_sUp^07eABd&UHic!%HBTw>lMr0qDD_;tc=Rb@&#X>c4eMhpUb!hAyI62 zT3@Dycd~~%(s>P&&V87$jJ%?W;z%}w zrX@ld+vJHb&6PIq6=Zr-Gb&<`7z)Z(t=iuam^d`|Le}08c+!( zZEI3YMv}v%crw&+!DQF0-AuxD3PSIk*)fTYkirs6ivMaxbwHCL3TFzbtum!v(Lrh*7C|Ux^m9Of2q|l?nDgeM5o_+CH3@U*_u+f77 zVG1I`U(>tOY|=+5P#N7VN3K^vHvxxFSU*9xVIe(IU~;k~ z$H`Ag{{b`!4^&DyPi@pu(FH2r%|9FV`3Ym@{!D1iy$T+&VT z5)L^BYMybFiHwUnVlMq@)2^fP?D*!LX$}WV{23-(*X@R0+4w*PngpMyOHPJ%JKP`)30uuY9g6Vz zp~#ndPM0h^7SxoK!6jpW4kP8FQ+~)pOe@>0X?aH5nYaa-0Bh%YuFfP~BdC?SbxavI zXND78p6II&U+?ro3yAv1y$>Db+Fw2_MAR*V7ZH4lF5Nu1(CT((^L=@KpLFx3>=A4v z zUaGfG8{M6cU5+93Y%4FWEBMUDOp0QOAwEn-XZBd{2DaLno~Qi!g&ps`w5;;ESQ9S@ zL!@Up%Hk^r^&hRA*eb7wdX$uHTqNq;hNtLRDOo`i;Nwgq&3UT)Ib4%nEJ*?tOC))L z>9UioG24R1@$&eSUJ1FzuT6B70L!QlZunO~n7`rMsrwmLqY zKahfDg01OceH(_c1}-P9HKg( zK33!g2zypGmi*bQ4l3a)||H{{Y| z>-G%sn+-`e)F#^~%h|wysG5zR`BbI2(Nh`3=-sTir>@+ZGcW$R3aP1sE1GfB*#!=sKXY{CTt9^%a$g5 zukZzp%y+EB#cH0p%(xC5JJU?+WUg^w{}o6Q%-f`~?wMnS55Ipoz<5Kl1 z;TC8U?A@O7Hz!olE8NTs=YlE;9wu?^+J$d&MCt+x+tAGolxDj5MRCTU8fFt-ks~d}OgV6)l>mr9$5(%gHs*?ecP0yw!wA zhRZv?INb`g?0&sDV4bKzTe1;;UBhVv^H&gA7*;tVAxh#I zmjbyb^ro?w(ZV!CsI~;J&0yJm<_m%85FxP3#;0VHewVyzL4?Ar3jFT3P8VSbIKx_b z6@ww3!E*O{F?;@{BM>nnu_#Yr8Nyz8aG|pc^sFMOH>_58*(DrWs|idJ$3mC^&{}!l z_{LrR5HTVlD)8j~UMpuBAp%@tE@XLVHEs`jmvT$LkQ9g{f|!!WMsAUMz~NWv2xo1=2Hj?C7smYbC8@)N^7d0Z%~0h$K9a zU^4)kZurFvQ0{jS>Mhw;7M8F`Y@}zr9UAr?n z{Z~HzWOnN10z{0+BJ+1L4^46`75)YSnC&!QStm9A1c}qqEr}tqc6g2$Jz3q4*O-c+ zn9{)ZsrJNR(Zo?2TK+YA=30GBsIM^#qN4g?H5Ti)ieUIVKM0K)(HwQte1xNU{7Jh= z5=2H+46xVpuxzU&6NHKEwl_}uca`O~d&=_Fk2noiJ7p6@U|oR*>x5k0z)d$Q)|A)% zt@AInwVgY%skayyC$0+YpSJ6}2$cOz7i(8ez*JKES2$87IC1Y(dzv*a$BGiwh|-sL zrh}@In}cX)vU7oQ;T$3^zSmt=S9SO6H}h7`eLay=?A!OcU%mRS>h2nV6|CU@4HzH(djFlb zouOM~Y17pXT*X&SAkY3@`O8wAvlXGgQ%z)R_Y=QAvK z7GUTcTycQN7f&G&0kcOW83XxwKlXQW#ACw}<9#6b@QvA5pS2YD^Ml<}elcLIAiP}U z1!W<@CPnm4mGgq*{G78itt$J`=-!={BHulEZR`*~B{Ca9IOMws9u-hQ`417Tmm^cm z6*lW;EHO*=yJ)3L0-xji3oAdto~J)WL}H{<c@PM&@BW<>en z>FryL@g*Z{w7(5Dq&Og<5jf{a5iL$8I_nv^Ml|%=Qu>}`g47B`q6js(;O*0pAZ0}E z=E-1tqpsb|I(p9=PEmu#LdXa~6f(xU06b$+f)vjyBJ^BlsSMVk!=CCyzYJg7X94em!2$UMhu|^^2=tb|f61+Fz5L*43jYyAcJEzELxGe7!XkTd5qN%b zG_BTPi_?1M*@-m_#zaDMLE%;8lqf!z> zB2ZN@Eu4_D{lOduRw-K7z#Kpi(DIPV;-UvY(yYG+JdEyfa$?)-Fa36B0)0S}muRjS znRz5?eBKLMeEQoY`BI`J6VJ>crF8^)0!E+rE)9vI{HHnDO!k@*7WOw8rFD=BsX(!= zwP-a}C`<7&7K<5$siO7;CzP<;1FeiJzz(xj9jJgwFxtOO$}I9N15&qj?Ocf4ixKP& zMFUF$-S8>XQfcgjRGVIh0t8ehX6IHSBpa2nn8v^6K#6swU5x)N2MVp5AskDYLx4hRWfp1Y zKtUji9>5Y?D+MJ6-Fu-lSw<6#r*zOWZ+{@_S`gP4nCj}&pIzF=6(I+DpoF*%Bv{A+ zN2Q`xy^Y1?;I-+5 z2j_O?;x-hserimd=0qKoq_EaHX}Nh6IWG-aouL7`Zo~OVdve?ob#T{-cm@(SG|iW2 z)^re(RCMuCii_(A#yo@`Lbic~ye%Aqic(jgy~o!^ZTd*VCq#}*y+sI(kwjZN#a=XQ z_VCoOC2gsz3Yn<3~IK8f9kq%-En$}GDuyd&M{ zWArRu^d$LD~s{6PPx^-TVH-+ zvNb)NK>;YcgJU=uUfnQfkDb`c4V)^#Rx>^krQD zBhl@Z7T&{h4)tfX^g*d<=Hn~Be|rc^fTbufnjW*INv*BFtDVARSBhV55nV6Uu z`1$zTIJf1?h82mWY?pr^yp5E%^- zLS@rv8lhSmdH&)R!{;ww5#}*6G6)L_FbE3?Fx-FigyGNcKMdkxA`E=IJcMEeEKhRO zeE;!-p`x*e;poY87#4sk&0t@51_ND9hU%6+1~xWUhMbfbhNuu3`=MAGN>sl;B^ei z4oNSnW4QO=F~hF4^B5R`(XnOceg=L%UItrB6R_o8;i(K!!9EN{*-1c$U1rEEsb{Dx zOb3S)K0vZVQuC`Bo&wXyww1HN=DGzXGN>rYGfeAgVEFd!J3|#PNNbYoz+8e2SeY3yOlYfO z03~FqMh(cYx9>hM>^pRv;S(_XI#`=AFfuVR90TT}pTB;??EgR@?tSeE2Y+12v#P006VQ*=01lC0_sl002ovPDHLkV1gJ(A0z+( diff --git a/static/images/pg-studio.png b/static/images/pg-studio.png deleted file mode 100644 index 188a9bfce94d76c7bbdab821f3cd48c2a70eeb7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741685 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 k#KRlXkf#v@5vmqGN4DJg zedH=>Gx1XARS;L{_OQ?GB1SpMAc z8Xv0}P*S36IsHkmvXrAj={#zANovEIMZ8$i3*ot0{~(IQKj{Q!Rjl!tyz2bnQ^kb@ zmgH^B9$7*2kYpYf+$R7}?0XQ1FtJG-^|YF%8Ksm>_V#7-IH_sGZjNvVmZ=R7+P{tW z1+w^o5^!Cx4S8vzr8yUbP?Nc1l1|GjLYE&zgMGd0Lz(v{kk0~wt{mj7;TQ2NTBGo$ z;G{Cvdl{#eDU5}Ek#{=nS8BviS4DE%h3oa+Qcg(E;TuPMxV@~`RH*T)SHmz-uGEuI zvEz7#`wS;fOpZje+%rI^h1Fvs9D~_$vZ;c0e3cLAp%u*dpsb@A+X2O1kb*o0?MDEz12p_NOB`7&#eCzl> zSCq8H>9EhJr8L_RG2DG!sP4lz5Bdg&ewxW+FT+&$5D-G7Irs zj(8NNE_=Sg!KY;q9iP6Lwb{+#vGF?b z(VJek5m+3^JX}&3(!n$oe&n=Vv6xM3AxT3R`p$|i_z8;)t$Q8AtI7!9W&O%iOE&fr z!TD=uAx|{-TVIC(r;i>nHC|f#C*_zjhhKN^95W7`9b3}lQP+or#1`dtg`8SKQrme! z3`37aHb~5pG(J3~|MQpZql+cvqkSoTLj~JU!QqRxA7vQvb|+YdjJ`61IxYr z`v<}FcN+zBlT{~m|bE?VQ(^N{)2g&4Yld)+tNZNFHg;stXrffX*L~ zp6@Cm3A&5CFFgR@?EI`8WV;H@<}KG;>du8MPhZ|kaTX+>}_JJ zNWi;J?9fkJptyy$lV#Q9u`MC=V7c25&?ktkkJ%?mbVf4XZ&=fquJ*(!u?5r#IIWir zjkkF5JDqKDWRhui%*<%Tq^gx__;p6GKz}A*B>aOsz^V4dBRol5FG}%vYaR<79lcQS z$5UpFhKD0LvOEpm=jNoI^To*u;~YcOs2ZC&Oo0a1iG+96ToMqu&oth<$~P;$k$~te zy2+hxaXB}uU2eYA@oxBMoJWt`VLbii(;lA}f2ODpsy{{?@`|+~mt8ewz zh_W6N-tn|k%XowcA0@j)r(CMt#;AEvB+M0VW6gAg*Q#2gFR7G+#Gwlzn)r22xi*k4 zF;lY*j8};?A=_~BUX5dNge=)7b@g(^Gbh1TFyhzL?kR^7O9 zBTo%>17sztMb_sUi8MKFZOG(|f?zb-JZ6)>vFSFAc5Kho;s}}#fB)2$^7Efm-(tor zuH*}tt-BV6i*~{{Py%V@um3$drp_7hi(hVx=N`- zM0EY!{ccs;n4>T~FGBXfvqFpwH3{>0M{DDpQAE5j-1h0&| zJhNrf-t)}R?@6V8x)CRny&`5nkf@@FO&8Ct>}&q!=awwgYSz~MabA;7jhc>h+0*)3 z9x=%Rj|!qxRolwiaH;(Xvcwm(`zMRr3=i^E6)~N6_bQWKEtV##;k8qx2d=PWw0@Mn zb2Thx>26qfcgzE`^pqdk$gm?+b9*`ZfNrfn1t?38JD>9$CC`qKzbGpq?-cjSiQVhs z#aBLGXrz%`jxn3epiGNKDx7Q_-`P@)(@ieD`sRpf>9d}40esSSJ$*q}J5r?g0(1P8 z3)Od}XZf~D4|@`C5ZSu@Sf-O7WzxHANOT#XgAV_3W@*p(JyjMHdHA4Cbb8KS%;ph9RMAI7Oh zdX^<8IixBX50Ds+@6wqH#$f6pR!IBe>5c~=g{OqeouDB$8QZbc8IdVHQ4&x`mb6Ev zus;!>U;gQ?5iO$woI-%644umNM+gB^*}ES!L7%MU3_Cm{LI@ZisccWzc1~5kin^&* zsu>(c%mQsCP5l=2JTz_ival5Gv%Vi%>v0(N{)Fl%C)>t#THwr{B|LGoRI~nOBD*nr zgVV+=F(~00?!jxX2DDM}WKpLQl}2s~0go_R$>@cNV%Y7*mau_R4#(Aj_9pj(>geY6 z$(llG)?(N1N)y>Dbe-a^t627{j zXD%IWDM8_RSMn`@h_F}L)IT2;m(W0PC{d~QPok(%sBF=BL8)P@&oG0uaX;{lGcvPtkV zWUlwuoq3m7fQo;X9qSW6iu7j~Wil*+&2~FSa)=BTYR^XVes+=Ug`a?G+N0nV$~yqW z4({0?kn@8(d>3_>S26nKGCy=i){DC{r+So)NRW`49-?j!gGpxe2fX zr^7LOD)M6iu?=JWHlnv{jq!&CcPr8scN?>M%l~_mj6aM~-cUJR2Ps55dmkn;g{|K@ z#!jt3b{9GXLL*eQ#URC2A;%VtH>1Q7b{edeT}xAO@#xf#6YLL13vK8LP8rr)(se0^ zxj{@-4fXgjOx}Ar68Rk=?RvB9SYG-8h1UK=A5+#+c`?zex;0Bl&1pbDV12u`HNWY2 z4CWxbz_cZ}<2S;C+*gE>mv7VPC4e=yT%`wL1uk?c-%Jv}(<_{jK-%K&l@hf)T~Dd# zMY#0Snh+ReLVv`uvvlUz!kG)*{P9uG=U4u(AMsh@J3z8t?Ke?OU$N=7eCuz-*=vs| zd>uw;B?!yLG-|C)A8QwZF}&GX&O1J8mF5zcA~LO`lP_2yJxUMm{1FR9h$E}x2>Unh zGhNNpSU=+4(#q5V^it|?MsA8;ba&>PE!_=X-*niSuVxax;cx;#WZ=GIQwkcmu7eol zUB@ADD{VV~!`M$x3_ISn1%!9J#EWwRyhG*DIc+x-lGg=PetM@oyMgkKe_~C;0#D+- zb*A#r00M&JQWhnMVSvg+zLJE~#^m!N8<&|ZQT*lj35{>Vikl zJ?r8Vgs`6kH=DwBHS10KNlslRpJlRS^~LLXw+7)pu3iO-Y=CO2r*_7woM-Nw4uO^G zHv4Lq(r`&o^rNWfu4bJkyN|ZU#OOB%zas#pQh&8iV;T?mR-rx!PeWT?&D67` z?syhbK#Vdt48Onc3W|xI8E+_C_Z6rVzFQ8$?KrI=2H~b?QSWM3-8c(%LW^}W<n)|VpSQ~enY z?S`XSH(54!#dkfg7UgdIFA|qzb}}i#g&w~S?*y<2=JPym_>LIbaD0MS{kyW2+#gt1 zW=ej(1^oOX8ZuI>QjYYAULR(2X8C=Jm%Dy(xNdo0U5dJ%Fa%xQIOp683j6W`?eFQf zk;1nzK%YqEy*(6$6-+YeAc$lZ1(H2sda z&6u>&KoAqfC5^UCfDj3k2Mh-O@ECG6_Hq@ZY~BET@yGW5K}n{1^&DT1Rk6d_P^CHD zVPYMcY00c=>8@OueKRQ&?*c|kax<~eq0NWPHCqEzfKc1Za6dlJuuWIbvgL%!u2fzu z_H26U4)L1d$7dX$&rURaJ4)Kd7oI+5@JD$@u#Vz+^uht(U_uAAm$rM-H zLy4Z~eaYBQf&{ZR*=$coHjo~HO!Q#ZtZXumIj&MTT3mMw1CPnTCw)2`7@%l4?5@p0 z7QSOhKNR)C4jwp1x>M;+iaPK7(lD)3!?hEU0_F@3Na+APByQs}9~PBtA`f$LW3Lz& z4aW1&Eu0gCG7+Ch)2(bg^*TS)eE7EcrSk?Svrdf+TZT<4-y=@5&kES`;u%a6wu6I= z4R&v${0uWrXEG(^gOH5opYhr@$BHe7Q^J$`E_zHO=WoB-%0ig*^GW!lT44Bl4tEer17Y<+1`VsPio+Sj|k&c`X{hYt_CS zQokgNjHS}Qxut+3*>wv&8hEYbycQ3+ZRaz>K^SpOAQbDJx$s*o6<~~S;1|5H&U27A z^yM|R>-U&k&8;lM1d6X~)#|7Ff#yR67AD0LO?s^Q6AHbzo{KrweTFl^JF$vpm38 z{YW4=&Rw6sKCQJ!Vleubu znaXpP;YJOzjBN=oyXES`M1kVRByr`pEjeV3jo^7f<;ke#+_-)PD>2s8nw5Z|Qdznq zarcQmeyPp?DDrmD5%z z<^31|DE66lC1Fo=sF;q)1S5VHo_t_(C=--)3WoXk_~fbQS5WUwi)W5bm^Q`*rQ@9` z$=Bc-ADu`3PA6HUlP-HB)*suI;X%Uha-T7lt(0h=ezBBnghSBP7tQG8R7MQLF3Sak1}QsTYw!qm|@Ih7z_Md;C;lo!4tmVT_=!cpM?}XW2}8JQQk?&szjUlJpX2); zH&+iK!iuG3d#c;yHdzTF!lReTi+cvX^q^Ze^?IIB(^tM>O6^my>tEbWThg6G zM&!=fqt$fXJ8OD_DH$o7pK%X5*4F}PaOJ!FXc$9USIyz65-g>^%2NlMba`t4@tqQ{ zGQ8lWTdVy`NQrQh9Kf^Qmhx7-tSevI(?PoI)yx}JSCgC0kN3WSwEbF{DKLCUh*uDZ z53`DVifOv6{NEFYfA*QrXDsc3$cc32wNWRENm^XSjZXs>xDR(^@dA$2=H*L_#t^z<#gJf?Nu9`d>E42#=^ zre{`x|J+;7WXG~OgOF3max_~FW%Zs^-0Y3*!T@;YbIL&gwENfE)ygW3x)1FRsj@8w zeADo&3=cXbjeg5=5a6PH4SC~1t-o}(J6I6{#Xhvs#h@e@__!}!(?*y0xW!E2P9+p| zi*mKs_kO<8yWJo6PB$m%Qd%4&nloNd2e_1EPO}?s>M`uM(MUh+j2tKw`F470ZDFge zgbP)ALDw3S>$B&*91)Z7a;$Dmh4TkL^)`ut7f{fO&j86?I(H7uqeX2FXG=%1SdKrK zV8psd$o#cv$}vpPTIlj2D9hsN`O%N8^5jLGz@Cbqs1|Exm_|^DPpKj=&kIXqg^6fM zNyudraUV+<^b&%k7a4CwW)2=J4y9mNqps_Z_4T}uETk2nGKyr(@@w~0cUA7Cm1w&j zu~?Q_R3<23<2Ss7Z-xbxYZ~9asw@273YZe+!|&W&ypA^F=qW>Qvq;j80Z4kxVnQyJ ze`&c}Nqi1878nvW%cO}76a8Kcup7~e(UC}MKZQnBmS(f+aG=%UNnGK1BfYU@R*-(Z z1}6+q`!e(~vo6{oS1#3eBp3c!r1PU@szy*X-{b?#)w6FQ4@kc~&~!F)C<>aVNricy z;o)(epODYAdG&uY?*^NE96|WK)G>9)nZQ zPTz*&nE&+az&#UnaL)TyTuiiD_PVs=YkjBP)euK9HA^OLlMo3}gJNbZRweDhGMvRO(xO=c9MIkb+RdC;>$qPaIi;3nW{ zk#LY41e3&P5Qd}ij^fwumm8dLu~d}JwW=6{I-9qAr7I*SDy7N9Er(e{)n4JuU7Jzb zOnP-Ip;KC{#MY}au1CDxDbsi=SAfZ?-m;lguOaY`2xq&%^wl-(%Ay|^nq8?u;f-A8 z1=%(dHoe3&W{7}6_@cQ&b>2$lG5w&AULWXUFcvc^^JfO+zyDo9p6cb|uAwu`dIWZ2-^;%~uXpfk zgXJ(EkM!R0p?g4fqp$CX^3&xIgDJn8=s(H5#;oo1d1mi=o7md zt5J}C9Sf7UH|!RF_J6TaL)oj{VW^nZV$1kGY9aNOi=keq=1lnQvZi@xNf4~IDP4bR zhf0@{NILi?qpyvqqM2`zd&(XDm-f3+?IRUL7=Tp@c>x)5KnP0kNpLc6+suygC zJ*2Boj&4XL_d|coS;_zl#J3>8aljMpB7CkTPs10wXT_G-`81XPj$jWFEzOLsDImPy zhP67E>mb8RGZ?u`b;+zP622K;{nn>>g6@5F!aP035}uO8?(f`nI<+Mi(G#Q4fMKT@xEYr<5Hqp%qV`Jl7hw; z^dI6hn;eX!&tCepE0Jl?9zwl?uju#B%ogX2IQIPh#qOh*Zb3s7s+`ieI&>8ULHW_~ ztQtkMh^-EkZL~Z7zH8Uum&q2dz~?%keas+CPbiZts_*Q8XfWGSZFwM-6ye2xe`jZ0 z=yW^W8I+?qZ6DVUDK|Jhl}UNl`b64fcV6h3aPX*yRMIG}Qm)Law3n}Nj}EyHR5RvL zPloI!O1KK<=9KhP83P|)a_0oYK%I^lAgop&Npl(nf8AJkfMja`C97@0F$~pDI+D3< zzQZ@69oGQXrHM>>0F7A4qx;`_JcUf|b6ZX3IZH>;_|y9_$N}cJhWf?kwtjbczgE@P zu|h&~i|$1+K&S|8vHVb@=XHq%`0kn89FTFDHy^eDU16kWMd_FO)O*jKL<$M5_9a@|-Yx#PSn9=DyMw+^I3*F@F+_fk zX2cSAD6gvfFv;PrU`xE-bRm(vpM2b>Q)D~Sp#t8v-^39h?2AGPvN;_|LPwv71byc9 zUeR##&}z9WN#3saWzR-Y8z1Vg*gX7mU%`jUo&T*^mQM2tpPaV45nUr6o#cB>${dkk z?Rd65nlvd<&|0mwVJeEtdN5(GnNf2$UF!8jsq2(p##X%2P4`sApJ6|YQ0+6NkBloH zlI01Kwh^X&4Nch4YLT_jQIV|BV9}#l;o!8N`bl)Qm8SQYMCFrieLmp@L^ z^;^2<-Co?BHdQ>1nphZRilH!;r9lCU^-b(_{pSWKhf!Xvz8WFYjPvlu{&n!#Vu;g& z23Au5JvM_GDKh zbAe8eeLuYW&EVaayJ%>6K?WJ`opPN*A5De?hZ}^3v@kN#Q&Y{c%-MatK(3|KC)PL5 zG&dUCp6u|qu<`Hrz2ry_o@>7B=SbD$iSu-K&?vq=V|=Tb`$eYjeaM1leT(VqNT#=m zqaS;_7D{VmW{!_t3~I3pUv>SUzwfyc7wn@Afh;a98OLH2d+oO;96td?-!(6u z3{&K@3}BOSzUfI|V>>~(reAkfNLzMyt}Q6QN3x(;)ystWUhaM7fZrj~rizeBTEBk* z@xIvR`8iyj%yGV5i-?vY7$I_aBfZCypWk&Gzpt_DHhs({=i5fei4D^`^Mt7y>*QGo z;wqG(GY$6Ha2yY&*>)>2J|EAx=u@?*covhj)I;~C?l z4Xyz*YdV+N$@1Ym1rgJfMv0TFpr}kE!2jGC8m{gY8LqL`g1F-zZa{UjmqYgMi^lp^s!^NHkL0S-gCr>vJFCey z(EtpmZM-i)neM$xY;J!vmbPb8X16sM7|46E=1VUt^9G`rSGfN4WX=^Kd3EDfvAu^b zA)Bgpn%gf)OE+L|9nOps+O02Pk{XRS@7(KR9$|O=NES?)<%}Fi<_v)N<%d|tB(-sC{&)Tymn4ld zKo6qpHFm~hCG_N6pwhD&sY-tAB#^^Z*66sa0Ey)+QF(3SP@U2|PIeVKzV%$bw~K|h zf9=2(re_-`kSlIqe-!;4ZqZOgw|Oi@jjvr|;9En&e(Z``D79I8KQmCZTHd=e2@zGb znYN{{j-A8AE(~szmfllavmL3*z|R@ixBltYuX@=g&OJmJ-`h@yQV`3Pbu*Rxs_NrB z|HkDu^l`qd&Rweazuo+kAMe$^k|SA6y!BXBpXFklrNu(~cP&ZA4_)Krdl^{Zp)og0 zs7iE9;685d`-S0hnfyry!|%;nUQQKL{j88hmu=(f++K{^S2#5l{C5np=A=O(bmOH|S6=}1_2PtBeA!T4mgJmwv#zECUmGjR%L z28|^hoNr5HDfE9+tgw6C7SvkPyDh#qQA}~`GLh~ynZyDMMc9`hLEp52#dV%A)Ff$L zzdwvNtnaiCPkgx|LOcL0ObJ4oO*UpN4LV<0aFVoVwmHrdS|e95H|@MB!<3G{4026Q zJ?6r)A|@zRvAJF*wUU&>oiU{WffMcSIHu=L<<6U`KPO-?q&@hZB=-v+R_}E2LWz3$>*+cBTqRw<2vE^q>6)+P|srjs;KL^sH{%)i_8IEFIS z0KU^?ajyjc?g-wJmAtn=JT5r0@B{clE0@H(kYgCDv=(%XDi4Ba*c|kB z9aIgxINpAo8^@gA{HifJ$A5{#V)U-2+`T%#q-o5}$X37Ts#E0DT6*#kxnbhZmrwmE zL0JwBOKH6-XQXC<@>9bm71O90n?{ue=WUCl+|_jRk(};II9-~ltn7O&E&JnIWMOA> zO%o*%Z{BD3VCLH%oEHaz3C(%Z)-atw-`a!G_|=uK5)C~Xx84< zW0pm`?(G2ICi96|t{%LYkfpSIW5n?VYsrVcbeM3}09@#T^W7(*X6MFQS$I?_OzKQ? ztH|nonw@%ML@w~g-e`>^RWy?FTEu#u|1=%X!zAeGedt&c&!jf~G~<=Kwbvx8QiiC# z`$ci>%QYBU>k)~e>tl)g2`2qXQ+~g8Vf;mdrDz|v2PlzaUilZZ*l3&mk76j@MGwVq zG>#Z*z}sw1;58E2Zd?5R6>sFzP1iJhBp5rj`AdYyL09VclZ)8Z5ySdwj5>=`ZuMLiZ29zNRl;IfY7z|6}IYPZJ+zq?|8tHVf}ott+Gm?(?VlQ)hEogdb-`7=121{f9QU! zsq;$0_+gnazEFx*YTH;9T`jm{d~xBrD@1SGr~BBl=7Y&jy3lN!dmn0DJQViB#Mu2$ zEY|O)O!&>sodsJdj&)nBgmD&n(0)lO{}VG0^&tFe68D+WW}1Q)WNe|7jopBN*KN6O zY-2OYI#|BY8E$!E`CGL=2K7ua+(P6P99pm7az(^i-CM7hJj!fwCvaOekc0iSa5I~F z6%M_cz+i8j7(+le+q1I`y1$Em^1WlUedc!udc-qqDugi8G&&A&b@t}k@zA-WQGOQb zd3wMNiIUXiI#cSX8fY>%eLYU~cJBlpcd@+b?sI@CD_d6=g>lo8sK;fhlekqcl-UK< z9ny-x=Fz3K7sKrNWS*W|m$}nY60H1`ltCo@>`A*re&VO0Rs^WI?@ry`Wp6G2u8O&R z1lTH{oK1butS&*fnc7~FRDOu~E}~Yi#AZs-DRGC@v?RsB<7h*%-obcC)%*zegoL$< zA1l$Kw-xo9`3Z#punXShp&Bi`|9`iXqkTxm2`gTyOG=sdp#$ zON(?G2bS!BtI&OmobN@Y*;`xWDMp2>?Q6%~fe%+6oOuxN`^e37che*1X`1cPe0eFq z^CK@MJoE%sEqVY&)!rNpHpD-#$~Kv^-Fa8m*tbHTC17w`M#~c0&}#-0I^9f98!oGH z<9Aw*j174)GW$xFJiqnOvb0`&K=8tf*Bq66(W^bQP&zkOs5ZgtvSp8&kc^OtKaqJf zGbaoADVDEN7!Of?b4k+TS!O7qQL#*!+l02s<*C(poanH;hnr}tS&srmaVvX2+Ej@J zTykZ+l{dzV4Y4e{4l+WM`C5&!dU*BG1b0EhlDYhlo==M;Fe%nEo z#K{h5yGziiQW{)4>rq&rMwgP@z-DVcqRFVjK2g^US$1?RnkwGhka-+{gEJZJ0jN?d zr7%=Qd@{eQO?}%3x89`R_M2d<-8e$1Ps_81=ul_zX1V($oRgXXsLWT(suQ)*fb|HS3$$GVS7-j$lq$F|8GB&>PcZr}BDKL66RlnLY2yKnQH zC0=(XD}`gm)RCtfJ%>Oai)cF4JF&n*^I3DIhVfL>LD|cTeN0Qm&kMVSm8!i(h+6d# zuAm|xBWNylN18kx8NWyFe5e=6S*Uv3lB}@%7Ys(riAGu3e7)vRW2IMHTzVZ89!Q)` zduk8#wMqd}kCwwb$u?~3C)xbQJr~l&udK~ozTN7E?M@zuE^=x5!j1|E0b%N1?520} zsodg)um>6E*7RY~NLI@83E_gqkw&H3#Lg3D^=2)Tt7?_QPqMPPK69&UaZwE_A+)6P zmzM3UXUK_uRm-nF@j4A|y7iSB1Viyvb2?4+md6T?O?mz-lF#V^Wf9%sf}>jiWzgI?t>Y&(d)~rym{Su%M2F;@UjI*Ijhl zl{D`3ivM=w_5N4r-djnDs+dFWq&^U?UJK^HdY9_zp`Wi+|GA0BG`VQn;jKd=gOf$x*kzXd<>gMk zJdgTjG0f1J3uZ`1->{V(JodVzCbn>nj{d=1nfPlnuJR9E9pnfCgJT^FIg#XM5{c+4p0C0ESfCk3PIi$At)s0pP zR7^BtVydu7%gTuJeR!?Ms+usqpEsTztZA|6c62fXeM=sL)6>r8 z%2P^~G(mJ-VK$&2LuwZ2dbTrUj*Z%QUBv}w$d!vD<}i5yx-)7q8uqz`_Q_6W z(eV4WIcg&8F3O_LW2xf<58u`KdKp>==*B>__sKC~V&Qmud&f@P{M|*fE5bfNX(rQW zFJCD=x^~{1f4xYhKsg>`sWbV3=^gp0n%deJl~uCJMrsi>X=!}7T7In2&3fdIzQp4^ zBfZjw4XKfhy|VWsS4lkbd@hF+p&;_32`AyYudXYRle=RFP7R%5%S#Y9_q_Mf|@9}jotIt6-0^r|gXe%f7m z4MV>QTY7#5-ozDG-sO-)Hw^H#f_H&;mmg#OipBB5jo$$)Btw#Q1 z@@kv=_iC6^VMaqQS3}Kohfq2f5j_D=96F4h4WctV{{waBpF}fbIoaA5xSXvUd-4;i zP%csJqIWh^RZEd)er;bO1q06#@O#P{Vk1CB1Ud%Rp)D6TNyEpQj|;X{+qmQCf2HVm zBi^Iv>%8C!Ofs5ZISG|}KIk2+V5hC@PqbW>p|IUXEvO`JHIazA1Ug@?91)cZpf46T zIFt8uuEhDeH-{6Ptpx|@Uy?&}-92)X%$LIL{T&hmLL4+<#^MH-vqzio5dVXf2UyIT zwplK@;nda$$3A3@M<1q60!viq?L7S4nH5D30nft^Rva^RYwwztc{)Wf4j>LgzF+)% zE_>-tH;ICO{U~~bv>w?aGgr1D$!@zQd!F?goUKC-E@OsYrU1{c9@W)J z=|yXAZ*N{QBa|61m*#P9t^xD*gGP^?TV7UfNW)sZqCeoqJmh|C0!OLlz<71y+4QKV zZ|1^A#*M&1D8AFO$toY3jdGa2x$(Kx&v-~u|Jv}G@Y}^;S^Hn!toe>OJ&n(T(LYPV-8>ee|if<%1!#55s%XTZ_ zf-&;_S8M&rwy)_VwHijg-N9T=7^iQVMGf$$KC7yXeCWZMg>1tS>M1HU>1LyUM+RjU zlr*=#tZyG?ZXUAJqQ&P_hv4F}ZYDYQTnhhL;q+6H*X^18 zrIWdb%qAa@ak>S;QAa0$hs)LJv@Pn$+RY%qTT=^V+^s^2G%D#O7bLZOtz9p+hritp za9q8Q>?rA+#p0Q#)vBSb;?o52$fgIr)FeNPvn5k!g5FS{MZdb=X1aH@XV4g|_$gNI z7@we^?lt7atlKoHfvl~BNmj^L-8qnu=^aFPpQPqyJ2t18eQnZ1c*}+M5BPct=y{%H zhjFydwdIVNHZoahK7}p#(hv7md9+-8^)`1F`4&z%{cDeRTy!y~Wj z=HI@pwbA)G(wKm8VjDvt0MR&jfIJ#}9w~XJLUE5LX6H3Glg!O?H!80Wc;tVEVf+i+ zh$Ok;3(Fr0@vYE!emYZR&tmS_&g*cMHxV;;D=9Vo%#6YPG%;eOG*6rfC^(N;DGw-b4#)AEp@(`Zzh4P zbdHO3b1Pv1^-HX08-|-WCpzhjgGOJ)gDVsCR37iHZseYu^p`|NJr;k__G4>k{Ha>RtWLG?A(Wo%W3ze*y1*O?Vdnf3Wp6jtg zjoN}&TS|q_CZJ~;Nwq3#)9vl)#Q2;EO+aS+@nslzKkD<~1r^}z>QcOU z8(KNKV`8ZEpeyll@}ZT=Q-3^}*MH`_{>!P2#(aR3wZI!QtN>jcT;Cx0OJkE%q@)8C zN~Mwe_-S6FzrHm9T(CE*_XCgdRD*Sz`A>4H(`4OOQ75K-k)U@h@4fTor3gBJ)^Ma~ z!@=t#vYnH)Qo4B%PBG>`suMU;#vnQJ>8ue#`$l*HFWrB=0eX`q8vJ5J)1k>~vg{Qm z5i3n;(@Pe~DRmP2^+D=x(9dQI0n?+G_f{6XKss6;^@5autsGPY32t(v6YFO=T%)BPJsd~jC-e4$HuNa0x_8uT7P7n9P(JmZnu-X){}zsnZr zmBH8B;Jldv67XROp{xyE#5oenkMT3op4n%7rwo;k5-EnA|B`;Hp7$yLf`GD1qd|1|LFb z<<^a_0oM}z7m^w&4m8yKLvP+xg~gs)^TA3tU9GNj+GxJ2Y6uZ4)n?6BY&v04)lJw1 z4h1h=+-?qtg%C!y-a^MiiwG9zlDghOT`*K#oPdfFU?Pedcc&cwW(S8K3-9p=co09c zRmDb`{8sw_i4}3aHHGMj{35b)y1$fm2c%R*LWtN{-G41d6>HT7qrJS;0hLq6Tc3vH zgeQekf86!~h^-dOqRnjCD{iaVC)ZgY(4#{dV@V5)I3Hx2`$#OzknKcz(c#J5Qc>T; zvH(Wtu-{Ik(TROQdA%`I$G7jII_3op!{x(;T}BpdZ8ebsTy@L@f8{?4|K=mZ3`XDK z!rF@H+Ii*+`*DCmoFIBLBjlo3v}!(74T7pysSEyH&KvMhL+&z?;Q+s<!q{DBL$Zq4#3FQ^^Y2I5L# zXW}(BQ;ah;wh83?u5Wa~G^Q$6vH(S05x`5l7h9DpvyN~K+ZE>Dg%Q}0jMw04%3Elv zG?=$55*KVKePIe^4C;6OX3q?Oa@tyLvr(UtPLYd)fy|Ev{8NAv^}wZ7M5mR$8L(U; zju{XCZc1)ixT!9sc{k^b{LM&3Y}5}}lp{$DVRINHx=m3gba#s0nV zLjj{sY8ZI)pSu!ul*9ec8FeASs5q_!P40;ht%EqliTQV-{R`cD{~jEQJRR7#ePK~^ z`XK4EDQo@D+au4w&`nTYeS`t5;2tNYzkQal@AbjSk9QX(?h=TL3@_rTrv8WO3$GFZ z^LuvkTpgh% zXM1oD=;mBV^j|I3^_U~6t{3d%hcIkZ$0MS`(0|S2fiW0Ord41WA2B1iMdU1>>~GZT z)5GhHYz$wp4cmUEO7yS0zXGE%(bg(Vc)e)tIKOfKuEc+SDEe@M`N`hW6d!oK$Mg6o zpY&f(qG|?X8e!@}CQ1UqHJMJfy7xD06@?4NM7&lu1|DS<%lr?0zJ4CnEnq`Te&bUS zF9ti9H^)`<=x-jSzyy}T!c}&G;PENxq<{IpSGJrQT;V>(wylQfU<~>^Uqb|B4v4~@BPbD zdC(>7gGktoo4V&>vOw~d>*f%(oksJb!_H6tOhaS!dw(^t`eVPc`=c78ASE zb&NCbA=boNi*(bMx9%Fz`JAiV>u#?Om7JfQsvI_{EdKWd|M=4bH1K?V;jm>IFn-14 zhJOQxa{liFpjWz$da5?Ow3fBxd;}_GeGhL}{H(GXhpFa&7bLg6gp=%|81yMl9B`xviQvSOTjsV9myj%y^whos^|4sS+VP^iNi+}d(hif(X zMD@7(5fHoR9E!hN{eN%cKR!`403{HgK;3QNz76%mQ~2xA{=wC**lV+9Rf`8^LEf=( z?Na>lzWm7reLSF^^j&XQ0v2quvhx2D0Q#SUzaCByE789{T)sb;U6#VNX8vHe;0BsW zC*S-ZEep6=xC4krzxtK(gxns_m{H#!{ENRshp^ z>-@*EEdxZe?KW|KJ5aG$&Z9s04}ZNUL)WfE!?DC*HSnBbgZ?wYzTUx{f1HPL|I^U@ zfgb(Q=G?tLB%Zwzw|EZx7&=*jzaH)XeO1^A>|l7wC9ae8T@)H!#(%njRFO}?A?2Vx1mtpnQQy$XKvpR0h> z?<%16zbf#*D)7H5@c;P~pa+H=B-s)&B_cvv&~_^%Kc?`~xt3_>tV>$9#4yN9v2vu0 zgu&xkl1*_tBhn)c_dA!DgYn0m8p8(gDJj_iNizoDB*_JIT99|)P4iFkc{|R_)qB8} zt`Cs6mp6S%h?K0RHE)`2q}xKl}d%vyW1_?(n&s*l$wL7FW6)SYVZ9F5QP7CiZs zmu}GGw&w8{k-+8jZ0VIWvuTR1&Om{hJSHLYqrAGwqW}+`!C%)gJpy_q4eDnZ zLDQQkM>wz6;-b`T?G}OI5(%$*!b!%a0^!dmTo(d8sG`)A*o(RfAwP6R()E;k$~%K! z+A7TU@ib0PXf`3Q01IL{1r009sJl+MJ=J+4*k>MrLUp%t{cZYU^ z8l7yw`f?bUGL>$}Tq%Y-?3~?7mEQR|k6K3P&921_;ICJvHOfe>rRkV{cAVDLV~LqP zGla2OY6wI)>)iBmkqwV&mTNMRyCI;eTB^Ztum(@?Tjdxc&P``^-W-ntFZ@NDeRuI< zRITxzQ`7XwZ0VOY^wxR5Ef@#6{Eq|EJm;xRA~VvP)`dFr-dLP^!woSo@5$aD0F-IE zdF&oL?8h<$3Ns+gp3W~GLzt=W2|kDtDl z&CR)8Iy=l&vuwZOb=&)VxHY1OP070$)|k0%ea*VH<%z?lG2RKnQB&a5jUPH+Wl*@f*U8G6#^>0FV z2B5_S`rdabCrfJ^-wdK`TQ22vAxpCI_2WGAR4iJYCt;Q7NM{qC9|&9=oG9?N=_xUz z7>tVhWPE+Z<7wt_p_t{>d-6BX9=y17)3*5_&GY#tlWDiVjQa7u1Opu}q0m`h(=Pmt zCnADL;Wd3YCH~>T`^tAChH%4(gLI~2w-d{fl=`e!ewA5AvOI6PU)Vmd2jc(hTa}t| zx6oCcsr@?l&0Xk=OikUnSq~g1@lR=^eD!PD zLI-!j>)u|ac^}`IYBF~G8qTPh6*TMW3c04Tn>JkW?IfC^(d<)mG#_6|o>H3Q83fDNJY>uLTe>Bd#3j5)W_Dhpqa@#*~a2>vY|83V$ z1x&NjfxY2D-U7==#yN(BbN62Rz8qMgvhZ?vzQv!VOuj2gXb45W3K{A4?|(`<9<{X& z+X(RPPySwSHf^#L*(>1nn=syNTQ8u4tl?YR76;ET9fb4lEE!nzX1F4lYZ_YEJvW9` z)`7he!+4nnS8NB;+W0MZ_Tl@$!{{c`EP(^lRN9Lg+l70r@zo;BZ$CMfvbK~f(XTCK z@Pb(a%8|Ill_IVY8aqQ$^qOlLbAo1JgT`dvXkGp6{r<{^KAsgsj1)c9!6$;ut1`nq zq|RMAd-wP$n9OMRJ}CPj%ftA2@$@+Am4u`%76 z+j5|KTO`{W+V$I^p_4x`b8D){CPi2zmKY;8Lfzw{`+M8P$*b8Eq@j=_&?qo+A~@x*U9Y_*~8mg3yFVR%S}|Owz8Kgq{aG z-!>OAmNy}9j#bBQqU(`HNsh**{9>4ip1ZU4V1(z!&@{ckPvi^AF5BuwGu!7aS&Pf4 zE;)*bYnFXK?)GDD8|1P}!R{6alY&G*Y_x9De)RI&33_Zu$UI@m8##Qs3Jj@DVKA^* zn#@{UuVpNZg|LcSr#hkQ`q^w`-jgb&u-|^uB1N)2f)dwUn;&rXgT0Hy&Ru1f{BwzM z!v`$mZB%)oPH`+yV>{4!Wx#CjvqT;FcwdVy2K}x$=78OxL}JC0tgN0BIFE^{8DC2` z_sm5(%)5CEG!c6o_g0!t^1?yDnv;Hi2e~r6K==pu4HvBy;!jQ=jp$%m`$9F`Y!)`w z7FF?h3)^58!zRV6o9(AB>NNEbpQZOh{_KXzWngrNnvPO2CKN4HxKy1bgP;V!h{I~- z`g2=r+vD>557doSgw_4zR))C$VmW_b!IGOOPtJA$6 zFAUtrcV9JKEIE1%)ktxr;1CaYqM1b#FG|g1SU34AIt)l;3N|`>2@1Ui=irPFNseTN zlezY^s@?!)PmqDBcUq03f0X|%AoTf$$gjjv9yw7YrqDAJx;2JwC&K|qg3Y#T7S@Dm zkX_F2`*}YKa&O|yKcQ+xMIezYrnVcXyr(0Vj&6)#lA*Rp??KfhX^r2}cR4(Q@ROHk zCg_2@$(6wM^>!diV1oUF$St8Q)cEFPg|emG{-TxshnLEDN`vFYFOa*d&jIL3rhm2O zmn+6|z%2g8dvj*qr?zF6+I}*xQG{jFc&RT@9cf^J6y+$dKd0%1xjHW}2OY`MeSLJ19dinr-yXlpODMSv-}Rw~eX_s`oGo6k?yRxB198H*&^x4WJaVsWFNu7~!+ zE$1=MPJVJh{AyQ9y4aJ5%a_grPc9{?OkZLo-C|PR#o41;s9a27YqHwlG9xdyQWS&6 z({CFVE3WUsTu=@0h*9#9T@-rC7>Xv5yR!FyNIw-i<{0jeO@6c@=KcGm9+}lz+nW3b z2IXmnmh^lXTChf~qpp3Eoo(2va4vsKaa)oL%1om==*vLJ7fL@x2^9pwr94_8Ev&L5ci_;hd^)dz)E??LqUY5$9J!1{Zow;Y3vhhP?iuig3ivx7)SAs^P4+*`CJU`*Nf-9&HBR5^--o9G?n(9*PK2 za9((sHuhAFOPX4$KyF(yY4DV`x)WkK9#xd(YaT=*fx1eyf&OBva6mVoh_g4pn z{5H=UW%Vr#_NF}M%KuEK@hkF!QoYF5pZKF2aWC>t;LDzu-|53SGjY+K)& zEJCcVJb;gMb>p^&`q@0qNo}dm3e-&IgW$mkHvgs;cm5BK*3D!~tmBA!8Jt3xSHAe0 zw0QOOF1SK~yKwccsLq({hcKWS7u6oXuXi!EUx;@C=0WypxI?3DN&XnNx4NL^r>eyw zWvrSEWQZ%G-~Myp8*%B^--!c~r+d|S_V@(-@fJ?bdR++VtKX!B+}0uV8E3ii(4L3K zWS8RZnH4ETrTppW)`g&Y(;kGMn7sL$kjM5693_c^RA;V{vqm8vlCD3T4+Sx7*N)7~ zDqL~vQm?PE%334u(;{WMqyzIph1BV&jLhy$_cba^b z<#zR-BFDY!mjK2l?)5b*RwIt%GE=fLzr$nB?LlZ=(=*?{;yjkd6V-4_aI0C%yMnK5 zVr>F0&6l*kr|Pc8GO!^1&XQtOkR)?;pnj3r}j&%WqlR26)m;|R3aXU zRcS35E_R&!l`TEKuwbE~@dTab@UPFi1kf@bp8mxN!o}cZTgWv#HCMr=gDN3elRBq8 z;)s@Uh2O?2u#D#Ok@niwinv33XfrhWKLuT@spoV0M%?z%*bhp>ARfm%|$TfXvfa>qR?BZGU1``i-}rP$0& z_|XB8b{Y8pgt9G=y|5on692#l%6|qp%l;?$8a;^KN5pAsN6WeJ)DK$kQv3PG_STn& z@rBGY`SG8Oh`FIf+5XR$$@mN*Ez^jb&+LhJY$qsPW`T(xwr3hM*MN581V68Jpc)cQ zde_+$d&m3o5O6n)6k5T}gV$d*!@SC!$GhYldb77T#seR*c;#MFv1jaw)zEJ{Ljmxb zqwhv0D3fPuy=W}|ts>mf3oSCd4(k{j^lCzAHrxLVw!hu8Ezq( zAnsH4ZtnIYQ3;=HN&7H4HM=PI!XxSX0Eqxv7tl79Q~O$H{Cn0};oZ55bV4DE9Uf{| z)@>uops-T80NzRzb5N(uubJ@}ixE?1EYsdG_wW5NU_&KSKskpHFes zfxJ7`5l@3NvIumRNh^!P@ES{tkPKL&}*O-@!1=}FA z(07%AaAO2qwm;w)7-h_A`fyF|RUL%@m9dkF*34E_4*v(R7A*h$ROK|5&OEdMO6zCM z2YY3Eq*#ymFKcvy03LfBcA4QoeV=l+-Y)L`qi=(XKrf#FCH3{pY#>2;3EMytZ3JpH zh#MKmH1q*7C4o)_uHMu)UYc#D+tiDwQ=#}i-%eLHxSL)}W??k_v8_NF{@6NgcV2m~vm6T9J={Pg zY@10*wUZanX>%JA&3;~YM-j2M<>1-P<(WatUAK|H`BHsQAsZoz%;?&4iIq+Kz%$`n z#AAA`?#~(-5I65wfeqsC=*8qQF*U%g4RmIfA6}`{Js$V;qwyFJSGuv?Z2wC0*FNbQcPj%4s`UcrujmGDNV2 zs5%=6{(LAqXYnZr;{@*7QgONOmZIjoBUX`pxGAYiuW;31$6!Mfn$S#c`kmF9!_jWN zu-rcoc@d^EotCN|OOa%Y08(927+CP8jP0i8iw3-P$#yfor-ek=8XE3CpT?Xn?~x-Q zvH}^vV#efqN8QK>sS}JNI{WsPja@HsO0o)`A?{LR=eXnvv{4*j?}rRLph&EfP3@Uu zvV|u-_75+tWDDXj{wXqP2LFKU&Yr)2+E(;#biRc9@ddDm^ayzHHF7~uPurxRskXmg zMV=)0=^@V!hK9DG)-Fgdo*|S3p6RzYRtDxg{kBv zR0dQ0;%1U>3F@o+9j@{&^*pLGUq$F&!2jeK+#{1f;RoAtgF-6cR9Snfh-SQRS1gn0 z8^G?rl6W|7L@D=+@2XLd#MNUmpCvukOB8V&muR1Q@vrWUPZwg+e^{h!H$w)q^)bzA zw!Q>3&SpQEe_*P?gbfu(UNFPpJks@pwgmi-oPZYLw~fR-wF-TotzG7xta3F2p5PRR>Cd?GV_(JqcR=FHVq} zfzp<}9&qn1(bz)l3ZH@hjGR89J{BMjc-81W?X@JM0|ds#HeBHawT)?<(1j(rN(Gq} zste#PD*`|Xq+nYpZ6)n!-8%KVcZJ+jg?bR%+g4foU2pTaSgDxK5P_m;aT*)+&4>Oo z4iwFtWlJRoGO2LH7Zp4@&d5Ugc{iVCN1@}g!=kQkbFjIURx3Nv`E8c_VBw)g$<%d< zm-JxYd=TBRqfvfswii*4u=2h|ZA(QuT#`%Ng;A8^} z&Xp{WI+1~jrGMR~ZnN2K%4U?{QYP;GMCe!<`AlUdJnSuNOP$S?#M@-8JoBiN{}Vz%*j7h+(X=$t={`=KH z><;MP>5uf8_L+4mZh}pO_?E|_vK)Y3tqzkW$Xs9%Y0vzF@Jt6?dg2n%ZDRFOsC+;` zQvju%WrI5LfpV-*@&x`j^LLx5+x(jVp~m_5QA?+x`N}*t_ishR#pi@CjHS*;}#Lhuj{+%F%fR zwBEf|!1Z7@^gw)TsyY_f+B0aRL=q^XWJ>$$9LT{(iZrF4+QcvRXZVan$?%_ss|6rj z&BC1=+FAEWeG3`Zdw`qTzUspIe=aO@>>6S}$8yDz( zOhseu9CBbPIP6!!Br!#EEZ_tn*akU@6xZ;pfmD8v<`1s&rR;LQG<%Y^$aZ%xe0+FKi^9)JM9Msw31 zo_S!EWzyr1hnAeZajtrUN~;CkI8``rFYdgbg-Lx-bCcM@i>{ZUN2f^erX-HtAUDf{ zNirZumr<)>{key^i0DcR+{DTF31rTp?TJX61pvSwlICxU1N#NOvPfx)I!|r#RRQy$ zp1B;**iLM#XaF6vKu*HzFe_{h=htse1K{6m8?oZjn4@B4Yv?bW1AQI{hZ6>*zOQvM zPK9>26#nL!412<{e&OJ^B5lRA*?-D`BgG}^`m>To*+IVPG30CCB)#_r7&rP>_iIn3)f3ElmJ& zU24X{P1kFK8LS6|7bX8WL-7L{YWVM3dF-5E8^)7Op$}la-(IIVvU;tm_i4{60ZewD zu2;jH4VB3k@v)P0AkDSC*z1KNd<*H=B@KD?$}OY!$L+&OF>Epp{Nw2K5`T{KU^LT# z?Wf?nUxm5ffzj+2%Ub}>W3lJ408V|M9h>|4x)(=xKVgfao^w>&AzzWqI*Ndv>Ip}m zN4+*C4URT7oFtAvZ$Ohapv}M2#Txy#F3{Z}{&1SD>SRkCDbq`Mehx3$K9yfFLdzy; zD(a$nHBYb7Q2R42>tf>mH~^?}yZ2;5X2%^+(l79|EPw5X%8$})RkO3u(&j)Y5LuaN z1*!$z%U$LAiCJ^d0pv;kWzTtr9faU2{d@C(qmqaGj-Q!`8dzHye*$o-EE4aV?2~+w zWZ=gp`!YeqPtv0mMJ$p5e|Scq26TYIx=;wBqgAZQ!yjrt#J|%!gIP>lvDhiMF0PoE z60+&=|2At_D3&^z`5A{S_xdcu4HhIT1KQ#8RWb1mzO}J}1R*Ef_an%anu>XV3BfhW zlBm0X9$Ouis*FSlB*LdL^H)bg<@Fh9mV_LCc)VdQ8hpvSsfnR*rE@hVKGEvUdGSxJ z8~26*t>OWH)&QP{e3PiL!qKrwbMHEJh@~8L!5bxRInk399CE>R$e{@I>Z zyM*d503<2HcJj&Ak8Dk%%{Fwo3(uH&G9&ZvV@+&*5GyNig^~ZVA?X4AZ&hHCtjq|p z%ilraOLxEj-;PUnB?A(jCK`rH%HlsegkSYkFc*@A@PG$01Ob;!96%6n`M$ybwCYm- zP@2-fPK$NXdkP$I4^?K7nWPN&3IyFMa9@rnu52D4vAa7UnLGU=^ZRpBHRRc8%_%Ny z)WX$>tHlISUKGmiL%NC_S7O|~L-7OR6t|>w4}rlwQbk0e*m3X4!X~eu4#y9-kw?Cm z8lPsH4bct1AjgahPSEzuz)e~Fv2NPa{Rg%U)}|137O|d#5Si zKL6r#^v+C&c5Z;g26YKQ)Jab7pH@2W=nMIMjUlVFUzMUBvAi(8H;jFbZ&Y{XlL|Qg zQ5{5PSUGGO+;87@KgE&KlHD^tX-GDk=E5V0nv03d71}3rh?fl*$HVsnC0cS^`-}pf z%Y8{gDPP&8bRroaFMrLy7*a0qZP?d(`nzWgx!N;LU05qu+@*zi3Vl1p?9tW~uqJme z@*hEx;weZSccJ~0b>eT@f+Tws2-6a} zhd&UrK1{D2I5-XIK(6N$5XiMDPFzQ&)rerimA~$pUaYd|eN>-zZ|Z`^K*}jSdzyYU z(msHa{)%A0HT_ z)du9|mcEk{TU);q_oecTqD@@fE5t?8$+)yHP*@D$S68aI*!?@`*U|ryU?#gfT>d49 z=bp?gZR_|C6x38~FX0L2n#X0WU8ph$)2@5nwibUv&RvwQu7Pbc*?XaDPuaU~s0&n? z$}cX`?ff`K4hdvRs*Kj zWe&NFa-iMu>!Fs{RvOPSVRRvun&>tf{cK~y)f#Vt70;)V z*&EF&mJ@CULQmz9=Q3fb${)Du-rvgkBD6kSs8K*-b5BiB4A)L z$tUgO<KMEEO{GhCf)YJ18mT)^V{^b$VYyV9QHMbF>T@`(c6 zufJ!!$EXF=3~*^#J<2&O>^oUY34U7xk#O(f)DWzqV)uURchrPm$H-P*aLFu3vu_6W zQ`q8wAtxKn#le>(@v&u=@!K8)0%aA^K?mKody|Y7k#H|Q!?U0GcOjT)xeM zg0rMZBqjq_tAKp{Z<2wjfU~Q!m6qnwVL1EBpT|%H&+cv)Rk8$N+w$EZ`xhAg%u^{0 zZ|CV&xO*dk%CAX|K#c01{r;W9m&EesRASh=;>}98;y-8=tNtx0^mBSf-8HF=!4~wE z`x+;sRJ%8lvAQL>e%5!KEs>@0Hohp^N8Np8VCk#cmc$B1-}d=gzZrlBwBVZQJTy+^ za%C$qD2h2mL*JB)%p|NSp9!IyC$4DcU`=^4^YLQw4L3QzD@Bzp{=FhN=gP@q^iLt^ z?x_{19rS!%|DJv*qGVcC^?$GC&&St+uCvDg!3n5)8P^w{mgv+*LX7sWK5n>ESuxe< zn8Ih=WLQUnzfjHgPQRTu^y3aI)lY#BfP3;Zk(8VM)TzpNLP!HVWhmYx&ey3j71KQ@ z3HpuYQW#)fmQC9)giZVJXq^sI#|=~oKj2KQ{TQ64wDjHHNbao^V>&~Fm}(0V@DfxQ z>!6y#?I+|C1s;os{r!gOU8U728!J!fjABS*PSSi;kuK6VqFSm_X&MnSS?%~Km6>!zJUMw0{&}_`L8wRzt))ly2JiIa)(`iZ8@^cUj@R- z0t8*e-YiL5v^?;oxY+Gf0c%a!U^}??}87ber@5E-i zh*fvc*6agdB1H0OP41E@RA3kP>#eyzLrU8DUv7MCGT!RIl9h!Y5bb=}A=}E=FKt<@ zQr5TojsS6yh!zW#@&8amZar;c8II|Lkh=fC{lj*pRl zMkVw<=>8S5xsyio@vr@*szOFi-DfamIk_#7$p-uC+W3k}XAVFzkJ!Hbeha{ln^f~n zS*)_|inBqub4JbHX7PtklTB$5kY?z^g}Wia{MTcw+*T^h_KtOt(@}}J=Kwz4xJsh^ z=U6@mJ}o~^vbJ*jeAct1(TUYkUK5x2znnG+xD z8lSQZdi|N7Pxh8*OZ-Uff2pe>z%1k5mmU{C02);yk6sTS*IOf?sgDNcdMGf+sg`1L zs8?!eUTRQrQwM!Z0bqeM>Yb80DSRHKGDXj2JW~8`*iK;bE!OK@DC10KKNTb-0Dc90 zW3tLCqt9fgp6EEx2@I@IjbV}a?qn|mR#gkzTlx->;A)>dspqKe1*S!Pd{d?~ncg3` zo&H$~fL{YC5DAaMhCi+dinb<1oae%oTi;%;WI89Iq_S4{O&9w>vl52|A9582KICKp zK7w~>){2pVN(+O?+x$$$cNYHFlKO)NkiT99kq<$(Mz?!Xl9*6s679JM5OFt!u}aLn z3{mHw##+Njn<-usrRs8E0>g@u%p$=qn(Yi+lL28_R*?)dp#=in3Xx~CVR%Rl# zT4;Ag1aG7c2h?=cgx|EBMJHD`$WC`WIVk=9A!!D3lwq4|XGyZvwn$kC_k-e-0P~KT ztNPNbxO0@`jV5MMm-x-)D=&Tb>+^uWa1!-U3NnkY^{RGWCQJhwV>v?awxGHC-b!zZ zQ{Y`Y?GZD<>~J? zW`(TVvH*hbw|IH+Un)HOa)ZL83}Q0>v^6D!q=T;+klZa0k8|9w!Hw+ zXb-^S>VX+KYAz#ww%wF!9NUimt;rU!gwXOAs>{WxoT;~;Mn&d*q=}Tc_~zErXb)^( zAZVLHna%LWipRuASPn?!rFMSJyK^bh@{Hnz+fRcf{V;fx1Zr$;MCxey!!OY8yeBpW zg_%Xe9Z=A*s@2T(l#-5?EHNbEX^Oc6&v-Zf27}=Ixv#O!#Ykw?A`du#|B98YE9)aH z8TLr2Ro`L=c_ibE39`XD>OHhCe;VS830TNp;;bN}8b?ZPPzH{>SA_*s(DBAC$VE7K zxsg#wOXTljZrWj+1IBq>?G5HnJtlBP-vl)qp@cVjcI8dmFKI8Vv9!hQTeV8A#!=XJ z9=FN*BXwyA%Mke7-^c1)yD#zyun48WSy#;2=;Z1%RM&&goWFPGEX^C$GxM$m9Spli z{2IWDMWvLq7X<<~`cFM?26yw+9_pJk*rb>hP84duT>gFsp`|@OcO!=26xD(fh9Iufg-229grz zt#2xB{xo5igUZPnhaatWWgQ^DNv2*&8~8b%6_^QGul*s03F<-%oyRdE7jmn-nq*lL-l_s@7uP#+w%n#;tcu1~R?MWxEKftXMi{MWP#^+_}hSOKXM zzkE?+!^66U{=*!lEZ?o@`hc|2YtkDv32KEy5aR4DP=-=Syvo%NV_y<90vgKI#%W2^ zoMaY{j$BOz>v9`loK4{L7dPzv&h%E7s%*m*b=0}{x`m{3B zz1c=xu6hFm+6iVHYSqQQmqlsj(M225A30i9E1vzS^65ueEtvN&60|7Pblya;Q(#B}GUU&_?ATdoi8B=nYtiStMpMx@0S zAwR^*JpJ8|-xLxoy_)fO3PYXz%T)HsC7QCII=?Wk2-1KIdGU_e+u_9ycE*8x7-k#3 zwJD1B$^XIafv;0~8Ppe(i}QbiGLf2eUCi=7sBOKF+5Y$Ch*DtCZEmd-X0zAPjEfcR zi)f3K{MOsCCamh%)QM!ncbDoHyr&zs>S{y$yb&XH+$5LMi)OAZZ&A!POirQ)6Awm| z%GYd9st@@zbh6*B8@(o}1??V=n-mR6oIXxTJBR1|7~2&HD2@#-lz?=QZ0G=X)e7qHty|Cm!ZuG<1j?Xdu7KN7Tf3y&i;}? z7IoShEjh|e=|WT^<+#2&<3*f%(({;aWl)5~4gO@w3H=P$W$Mjl<@|o(Jwgl|yZ@k- zxuVbT$Ja{?7q_MnmdZvm&sjhUwk(lK*9{lXXFX9~-Dl`GEd;ekbg?ffe=oXHNDWM^ ze5Mq^o3L^d2V(sYgTZhTC$r?*7C#9z;-#QDtYPlx8f0#xImlT>N2F7 zp>XAVdyL>zNj4`D)pGMWLV8=IACCzZ(qtVAPO;FYtRzoky|K-5D6r1=y|Ar`Xk>rD z$jxsF2Wz%jAoieRw{t}>)gi~I{-%c9*AWlPhh2f4gJBV0E*@d3AsUm^|B882+#wq- zO{l=*-2-X^IpX()KrWBau)FM!jDCN)$i}p_ENI8F#A)RrcV_?FIb1J4k^hZr)U+wn z9lw2N5NGEdzrGusLYf`#-r5w4Zx^={$!oB;?t}M*Xuwge_GilyyBB_PWw~2zZ0v?b zHZ9&%gXLe7E4K7sCY7)^OmD;LbD#=CPIPjMtuOD_H?0`JP|ED!sb)N$IV>`Z4`Z{! zIx`^UloC++zHn+|wDFB#%ij7%REzS%Uy=toBo7;HokbD!m89BlCDnd|vLqqhZBadm zD@oBy*X{bxKQgHkmdUO%^R5ELM=1YTzci`*pa{TTh6~%!6|+>xS`J638IE>bOSVf0)iRyP z`Em=1&1*MYMTN9pEbG3ge#Vl&-@^x|)c zs=Y;CUNgBq*W{crG^Tv)9>|_cO>>pmm^`-|<{m1+{jYonJdld`Gruql-Xb{Ko$qhWanEJ9Q z{L7STW--TYz+6A$hn z!3P;QXnzPXnM)gL0i!i2}*|j;d zM(ZKB#FV0-S;dxqw4Q&aWJ(sitg^YgA0pD5ZG27*R0l$qvQEPrJrLeVY0A5Jlzr#z zWL!&%!vp164-7ZEwzz3NQK8I8O7DK~agd9xk@3+h8+-d6_s@Gz!{klw@4%E+5t#dj zz1PXhi2d5@TuHoPdGm4-%ahhZ7rG4nD=J+&lk{tX;*jWz3|q2_jdGTVz%86>yY;lY z?d3~g5li#u7lr>Q6@IBOtB7gT9~Xgi1V&oly@I_~vyXd~3I0gBhVjLoQ_((PojO(zsmyG_Eo;oydcCt0q|3t2luUKq>3`jh1h zpAf7+wi#>AN_qbmyfqFosCULz#5;>r7ZY9f94&%SUbNI-`PJvKI1Cb$65S+PlCVAn z_uO98uc!2wi*1F{W#XxeIC$^+4q3__r-LU`PKvAqTy?g}U_DGlwL}K6-6?B1EnJYLO;cnXHhyPO@*Qip_ zfK4Wxox6WoDDh1V>PQ!AB&|qc8!;L++^^cmAC^mG72Y2=6&Hd}Hhf!lXPNGkgAP%q zptb^oN8EIELooESmm304x-#>w_hpg7V*fk{`QRkjOf2;uy^lXXB7+9+$jT1e8Zs1G zW%;*7`yPeuJdH3-;w#RMkovNqxq9Fo@LAr2V`u$XY^Hc9ma|jh=+BGh9T~na`6{mt zKI}zwiN1>P#**k7#V~1eVk!Br3AP1Lov!uvWF2Ex$&Az-@3-j?W6JofZ=0qhdA0A= z@3+l`z_U-@2=s+WEL|>u@I54=$f~;{F2oJ;>)wo4`!(}ikM1vgAYLp_a102PTt@-p z+i#>q6{OJ;o;s(Uf8PgL0w*Y}txV<6N90%+d_t^uz*a)SiFFwVoAXD^!mY^ti~q(K zuX#dJx}xhh&wH)i6{~8yfAc|)WQ|21H_iCKtcj$}~_v7SJuEFK9r$DST6HU#IWMtQ}$+b#mv(v-reXN@C|Sn&Y0`oTES!o zRhi73XKqU`h0F)2a}18rG+9m-vDNdvdG-l$f(_#DQ0Y_!7SUP2dMlem@5<+$eG<+p z{P1pTUZ5Oc83TzAr`#&9CFMUm;eSj5EpND2W2+X#TjQwcS zaaQ&rIq(mV&lgj)q(`mxWzUg6*^q&Bu6`p@TZ~#@myI%;_cxO+YPC03r_c2;Ac?n{nk;L*b#L z-|P+J07ge1V*g0}>!`YDY6H7(Vd_Jz^_V(bUP~v0{XZBsME?~4VXweTDzuNwOmcpY zT2O-C{-u%nr6XPmA25#Ij$sk4=4blKlp{yn$v5#rUDITZTzW~P^X7iEvECws$S|td z6a{gKWJwT>_MuFlFcLa%R*}eGb(x&c5%J&1CtO#-J>JPFI0<4ZvSNp-t~uZ)xEjXT}tnNTs*zYs6pOxDL@ZU* z7brC{ee&-@<2aSXJvx4TZ z4VYa@h8ZO`@WJu04D>;o5UJEt(f$(W0DLZ9apLjwQT#^5$lQ}%Txq7#E0(wK_IIR` za74+tpt7JE%=1ghReTI^?^@@FebCW8b6Y+ZZQ)l+Vd^G)Y_B{tT}@W^_XT6Tv?k7+ zPoplrCbRs7I-@kYtWVSerTm1s202Ts0~gbiV7?Vecbz*yJ{MNl5fy1yQSP}{;=MN9 z&Ex0WnA~Mh@hlKOX&dydU3!0aowcY>ZA;&n#}^sqzA6n2tM(jbOa5RDclHZI1r2)Y ze|J6623(0AVzvV*-s&s*RUX_KZ>EZx6HgjNyH1@L(eCZKfI*b!q|LlA=QiiDlB4Ue7>4kxJ8UZL7e!ZbuCbQG;-?-*GMGi^4cC9Gcg`nxZ)D;ca7AM_e}?XjJb5iz z2@2ce!gU|e`PElgj~8DRTqg(?WnGVHzx6+%u}=A4p>d@`@3?F_=f)04XYV%r=x3a( z*P24D=Iz-ObGDVNpk-e{>A@; zh9B-`W*`yARn7#b;Z(Zmf}%CM)X2V)m+g#?@Q-cW6Yanf4;EhbF+BRP;aVb__}OvK zUpKz}k3cTb17xFS1)|*HlzX zdAo35$b!t;Cs^4YF_Y_>rqTJ*Om+!aIyLdvrgCJG9QV{q~kNH?2rZ#9-22+Zu^>#fl@Nj z*QTH51r5#049r3BWzx$PZ`lpeSl-JbVaE;kC&mSp1If~DTt z4+#!^g7`V>C6jGT(p~M@nH7yn%mPmIG|7)9*_IznyJT39 z+~*Br(|2BzB_@rNpOIJF>8_QjpE+=}ZVfXUSo{RgepFO$W*#HaelWE6WVZ})m0s4^ z-Usoiy&H3nU2CeIY0NQw+@NA~Bv$t5&0proSfDMD>0!(6OBv!{iu4Lm(eSP-BN8MA z9(}IAZ0QG*b;eqt`d>^P{19-2xy5eF=4Xs`I)@5ru`XZSLpz2_x71rvt3_c-cl9<5 zlRnU_T+_YK-!M&FfYsYiJM8y26{{HKTN|a(ENt-UXREQpA;{9QOhT;MO?D$h3moR` z@2WRY8C}QYnYkCPA}`!~{wq70zgJeNMy0^=#3u*7KYKA? z@<93)J$YTKG6-SYr**7FnC&WTk-YR8#h!K~)D@aH${>xVe#JmP@VkrOwq-)-^#J*8 z#&KX>?|@W8lE#zc;{+?y2iY+fwkJBPBsMKE;}fIE+&|yfFzr7yR*|NY&zcu~>+R1N zt;)&lHsllKqd&wv(F5n@MTZj-$+KZwksAAB%~7mU<&O^o!nUdk*D0A#?5iva?ADpo zQ*2N{t_xN?AEEa_;g*`aFac4Yw{yYGOp0a$1k)9gKxu$mrUr7+=z->u(NiuHU>hCi z&0P*X+mW2_?&a|FT8@Quf8K-9!_Z3T6&abcSfxc(TC)5%(IAMfPzE3jH}4&&#)GS27j;j66JFoGSBi`mas6F*=P@E z#jLXGsvZ+iGmT`k+YoU-+!`zQ^B%drHAczw&sV3qoJXtu6MoD;><^dc9B3RGiy-Wk zEdY&7y~%k}W6-bD0rvCm5(LMLrty-H z`AA^_-EioX(ORE@L%0kf0DZ5!<&mBG6-nZN5}CUcLpi~E(C?0adHX_0DHh+1IQk8H z^?|n+>-Z)RY~pwm;gx@-$UB%eF=~F!-DsJLpI!|T;?Ax@hn>RuA2om{y8s5~Uk>rAK$gG9I z9?(f~J68AET-hhq@=_K-MGXYy=(^4GSDryR_%}Ms_OGun~NptGS zoHHo6;$KS{TR&Iw+U5xJ@tO2tY@uHdY8fjA2 zEs~hAf<6M1H)b9_Nm&0D_UY>bXk#07#@GcJo!>(WX%fRWSF?ej((V?ip(bMyc7($1 z;4@tI%L1Ec6>*WXS#{DF6IM0E^kufg$_SgNwDlKVCdqUsz;LGQjV6pz5=UkAS~ZoG z&83uD&BDXWq6IXq1$-^81yz@p?Wa3JA650-S~vT9cqEl@lU;4B&bW|f#o-)7q^7x@ zT=I+mh@XR^H1_}}Gp@1rGI&W`V*Gx?C%Yy$<6;6^KtsrN{rv0xo|r+9`=5xTdl4#PGM_+Qx8**ooulpVAk0mh3<*{R=i6jw&=?I3eC%$!odJQW zKFAX`Nr?e~eMHgk&a7lj(={>K8WQT;|HafF)0CWQL`mJZI*d;xL5ZF;xwrF(J zEk%|hPM8kjE|10?k=eZTmtso>(v9`FO$+8Q^9{A@&&s%@^J_G*CC4%F3J9;Y^R2q+ zEiv$>=Tts5nFvCpp2+`HRgx>UcmBy^TI+aZRA~$|?8=Q&nr=?I(xc^A2j9E2f`~)2 zw<)oy(;B&v=4Y@%A1@c{`0J`3F7MakSg`}RJ|it2keDWt3_(}fsjTUXAc zREcyyTt-m1Id^b%x~mGVKg!-kJd9i~T zWF2UMfevVq(Nd#R!t8wUEaUSp&Do`Ot3S6r;2qr8}`%^cG&A4v_elUHavC zN9X-UIi3<#o}Lb{QKcO7z4=orcmUDqdnLIO%i`3;&`_AOHn)1AyA{!7*>QjyAtVA{ z$ah{PzDDzm-0o1d6V#j{SpP# zSien22C~S{?-zYuQ9h+Da;g`dW6Wlx-mWu#e{K+HKe{4BK!^BCB(I|qPH?bDYI43h z=Q@*eHyBg5-QMXjGIA-FcKb_z22n}Dn|sKV67->PAjy9}yCqT*N2^`3V~vmH#+d!u zkx*Phbd|kXP`*C@rk%>C>ZhF0kx;6M!cmlP$F7_}2yNWUSi@gf`KO7gUXArnSg z20Af2gfD0ATKTDIgPtR}8;-S~vJ;|zAVxbMv!-DF<(*QiXJ}n>)?)_o=bX-Kb`ebhMmbZ>#R!Gs4>3bz~)_WEi| z6XL%MQiFtd1V=@XHN|yb`V>yjn05)sA5@_?D+}L!g&E)(!kZ|cyNb{$xr1q<9OWST zWImu*_Uc%5FI?s4&)JA;^t2SN5TE_y`k`=vwB?r8xafJ2EHl-tHBcGSh6l{$uDV2M z>xhK*J^kuaoQ3a;)&c@LeF`dsk{2iXP~i)*!n z8EhARL<`EuG!oxRJy5f~!H)w``i_2A8KdwfY!~TZn4%KqgyMa3*>-g6T*O%_^MrZm z--YW%=&l2%RfE@T<-!luY{k3N4~oAG1{rts2PPX;QI~NP*U2HqT1OM}p0zg_3c55? z{gnY{lp{_}t5HtmZG9uOT`8;YgMwE-*$P6xh~Wl4rbPa+l|SQ&UqQP%P#<+R)YAjd z(iZn8C0gLk&HI253Y03jik`wwZhX)01x#NhOH0LJ=2)HVUB=_xgp$lU!ma956p_7V z>P4YhFWVaz%is4G_B_`%@Nb7{Sl+}13JEQ3+x5x`kkpW4R_K)`Hs*%WGo3491u^(%@P-(pPR|23D)vCf#9gCMo@2koN?=m`suR z+ZkS7cN5b(va1phNlkdINKO~Zth7VeINl~jjs(nV+b<;z85(mL+T!>jBWo3FMykIp zSHEkxGF4nLg}a47qErHYZ)nqfvGX)w!d%ie4m^kx^8UN*G#SHpdUk88|BTUfWXTU| zxQwCm^&8dDMFU|e=|J%N?<@o|LYvd`O{2@cyb*$VGU9x?^_lF==8AhdVwc6;q$cV& z>KNhY1|`en(0Uv(H$I)7K9l$2uSB)g=&%v&p})Flai71U4!aAEH2n~g%r8-iYR=eY z<=`rPJt5Cxd+=*=b-HEqYzA8Uv1O;TIb1ghsEjMyL&px)Z1bOJJGze3MSj2Lm7Nza z*P}7O{Z0M|v)q$Fg*Waw8+rQ2cui{uvPg!*?LYXID1v}V@%Hy0LR80a&)&j<5sIU- zr!(M;(e+H{j*IN!HJCTlkI9%e)4>!El8o6zTGs2yxcWC6z6gGiW;|1CWJ}p}Yhjnz z!DE`orpMu`Ra4t9%8qp}0OVe{_)?ws0`=>86)hK(plJGY8!xs@F@xkVe(Yu73ou4O zP|u%D!u9gzb$+<6=c2WE&K#2Ro*;b{?yzgOzu(I`OSHZgf35QU2}zBt!c`{hzVF#@ zNRIa28YmJ~+=lXF%H4E`-CMzuFX}SoMEZVNhAerL7YeJ$qc<^;Rb2LeTgBcd2NKfw7_?vMLG;^> zX8PNC?-eNo&hvM;ep0r5KwJ{{)<#osgNd-3wPtnq@y+nso_)qgLXCd+Q{KjBp@g_$ z?&E3=1Lodc{u`y9_1&{8;|Dh&-uNff=Uy*K?2<|LBSiTjAm%oHH*?}lOP2RbnrZH} z+%CIrKfQI}1>Ta!UN%;UC4LWMZADIg*>s#mo^|9G&G6@V@4K(fL``)+={<*J)`l=1 zOQDAm)cz{D1zeg*Yn}6hJ?9hTB+W)-R2&ZxUk`f~1(C8(R+i)kEyD93dpWV1Z!%Gs zFbf4o$zD$hj~`wm05#8jSh=F}+o`{UWBT#>P}NkF2y!wRQvldj>}awkV2c04$9Lcn z(lQ~*$rzSOoYEzP^QCZq3Vey!`efkdZoFj*%m`DB;5K$}F)?WL7QN3N`t%U)n3)%Y zH&RgYh0oAsC;9$5VeHbwx2o@AyV^15s3Mlbe3O~ttRCyv`ppY2X=o5xM|=J z>R8vHA8>8FclxhAz_sUq(8+eZ!}sKk<_A{_h3;WZ%H0sxD=#-H-Wo10-BLiEp+bbc z%0v|>0V8w#4UzmVL+{$#>qjxRB-tqt4>{tZ5ALMsv)dO&M`BZ}4ez$C|oj zQnv+;`z{HWB&{l44^VEK;gQh?m4A^tLQ)Xo6gAKYXk~8;vdQE~x$)F`8L0j|MPMng zR*>DlvPXw#CGgJf@4Osd`XdQ5!_ zacwY>fF{5w-`f(7dyO4>?V4&nrkjeq5Jt}A@)>e&pFPLW1p`-vkjQr}@`EV1^2Tgt zrVYEr%Cpx**ZIYM{p8aw$KvtBy3-OEJv;Hxs@f=_>kxC&D?uqEb5%C$;ZyC@OM;$M zGV2GG@T$DsWOHDIQM?l|5hzdmtB8!#COHo4YIQ4WXdU5aG?-~2vUT0Ak*1FST-V4@ z?=Xm%ET6n)h3Lhv5QbKK+cCtQIAEOA&_l0hAJQ#WU=9~&K-vMSrfD00+ad454~3%x z$UD}eUI^HL+Y`I{A<5HMr8Kxn?0h;4%48|lismOWZ&#{%0-|tP)$9Aj(rx6w^v+Rd zOnVOS>Np#Mw%;TA%M2HJ$D)E+kgR5e*yE~N^|0|n7}Q*<%~b!cMRe(QRb2@_zdrBw zVa)F9p8?qvMpm!h$6a|x4f=e(9-P%_cdu^mbU$>)mm>uVMz5TxaoGsmy)6)BuddTdCIhQ zP~-3(|I!Gw<|ymWyr;AC70WcU@VHIZAfN$d8snmOZqy+5SUax;UgZBb`e&$RnYv1D z{SH~Gj>Q)HU#1s!QGt9@bS0v79HT$K33KhNZNC~n8M;_Y{e(Kv&vq#gu(f`&)8n6~ z+XcH4p8g4a!xcCYy{Vn>o5J_%EgJ7jLLjsC>TnZ!==9C8be5QS+qTLWS%hc}cV@cu zkUTauCk5eG0QbH9&2bo7Q+3sQ0upF9e4Hjk(V*!R0mmy0CFGpJ8XyC3OI~u>_EOB3 z(Y?X5v3#%Mhx6NgaFHXy>{4wAWQ8oW(3RH^tK)iYzKCQYY2$yx0H*(h0fvF80CQ7e zI*k^@{qWHsrWmW_1$@DrPi&WEkN>RWy3f$%gh;kNB;UaTws4_}w;W)d*M_)P{p}QG zS5{14K&JA~FyQmLy4-6k!+1$5r8*Y5HhA}bb71dHq|s!|%Yu4sJ4Ai&yExyufjwlF z3iA}%km7-#oH0(q6FNT>V+9J_e8x%Bjpykn>mwml62*0VbC2nYt*k)RtL+IqwgjSA zT%_q&fy`72)%EW<4k=luFvu@0fmt9$Sr+W=OK{JaAuUiiMbb6*Mv~yZ(ukz+XERPF!J_jn)|}u&OfaIXr#W?-mM)+5~3yEH}R7kv;a* z@UUuhgCbHg{}y$5s)>AjLSrMW4$I*7PB2;`o zjcsP9;^r^x-qrQgNlWlo1GAe9gF0hB8*}pCMA{Gyv{Hp}W(^3AmQyyr!zdw=;(L>I z9zGqqv!Z(q{JUazuC$(KTYA11sW+X*wkt|xSS2%5vp+8yB^)cYSwgRXpv1L&QkpEe zLua9$29xAPB7^x?!JbJ-rfVDPzP62?J#FYn_o<{bC2@ZlVK{|D2lkK?K2$O_MuC%< z+?4zwl&YY&U#+Lqd>>wv)QhlzmY?QrLoM{7 z5LZTcvjRCwhRv2zSu5Kx1zV7Q^s^RB|ORP5|$I-^Ss=Dn0z&U9hCHH}VHx@ALPrb&Sjl02#2Q7q zGY6S5v;g{b$G>~a4IEVMUt)W^Tscr=4smu(Nx6c*mA}e_FVTV%mhav&wm+LDouY#s z?9UrFk6Mj7d6YyImha~$>)ax0i=i)b7k3ly+Ag30&d8rFTmxn2Xcf-DDOp~Qwg-%0) z-v1Y^|Gy=o0$3uHf#BX?(Hs52P0Jk(LAi^Lg;Mh8%LUOaEPgIpz4_k)uU61N(+zw) zFEy*Ad_d7NwV?$I2|8~NrY2-prJOicjuOpt+(+A0ww?@;@|16<+S!Dmw-sf18w5}F zZ}6I}#+nQ8C5>OX4Os)D^cEVTp~8THmS8bL*)><%?4rJ*XQYI*fubNU2!0 z9^RSr^qX$XvAnql)3QY)ttPH$uDMFv)hz1h+AEfG`F|v9&Q5PgDdJ+F%PE_>)t`a9n%_}kdg(r&hPW#1U>s9 zCxs+jJGv@z(>epW+&^!W`U4mwFYo6CE$bYnW6ANsvdeo({HtM^PM|u`vgiz$Ps8+L zVdlBVr%j2%WRDrVq?=(I0S+O8x5 zXPK@<+x9vnGI41ME|T_wDjLCM=aZfL;)I*HsSNgE9*t&OF*8IuO88n;3KG<)sj(yC z$X>J?Y0AC`ez-!Qj^9o8Pi++n2IicK-43iBCeIqdd$qq2qu4X_4_3S;6x8DJOuN!a z-hT+2es@T55fNun=Gy{f-i1)+e1?#ALPROa^*i)M?8$ebC@0sYCvD04eAkO#Ea~mP zJxk=2q|;71H36?%Q?6W}Q*foC|I(7J*${!20 zeXSCa(9y7wqT844v%r9YmJ$-2eD*@))Q}P0UU5cp_CIUM&m-P!a`s$qAHz+m*3UO&mCYeiB{mZcHqai=CO}?o5Tny?a}4v>7H#kXBiR*HO#HFm zx%)p0Z0}zSY&He3zgKz1LV zExvF(!Bi%8`uoTY(c@9h{V z@bk2|z2LTs`QHaXeK7{Uh_akvu}9U)9k^U!nDSQf>56&XJ6!2h#v&Ke??05%OkCxp z8XoRb3l^}+drUL!ih%n^?}EXsPSUTqJ?OA&|CD5^Ve$`1@|CK@Nd0)FJb`T{_(K1C z(x0ev*2!O%*Rtnde;hOl9W{&$GX$LcYDZjm&hI#$`~0b6Yvvej|EpbaC$e+EBoO!s zc2)P0mcS7=FU0Y=gLsQV9*iJXgb~mEwg@Yhm0C|l<5wV$SPJuzr|8vK{vi(m%@DWu z9WFr!{dAc^p3$}2D&Z+WCuL%l!zQY<3yb>qn_9jxKodjQ@o^s$xB*-R*2cy&Y^cl51&%NRRvX__L-`#=~HT& zS+kUbXp$;eKQ^4xHHkgWk7I2p`G8`OF1^GAapUdKe2<>60=qO3Z)z1q7O zZtH;|!s0ib>=rQzcD8*ga#;h5m1qPOlBlst2Vc$$|7{RElLV|CM|geN&-V*V>Aff_ z&i>Jsy-)W2=W+0(O&i)|I_^blwrJiYx?-MRvJ7uc;e&nZXAnvaQ5k$ulXJ1p_U%-=qBh{NL!d2U;DPa$?Rdwgj z7`zN_TJo%7i@iDe!ml3`N=>E+sT*Qx${X6fJ=^qO8)@6FwP@1G0yEdA11-%HhGwG2 zlmTJ~Nd)^X#th3pI{jKk#c5y#ZZIgL3UqL_1m|skfTaD>dCy^I8TvnCrmV7V9=;88JeK7s`{A5R?3menNF12D##y?2eO@<; zfX#XP%m{bEm>HRa|D^)OA7euZ?0Sopqqk^>Ta%4R(ZWmOs^PLV9&$Awn9JVDD814A zo?N??A6JNo|KqCbwd5mR#LeIN&#qD>wIn-rb8_B#ZXkE@F1#eGPO$H47A7|`4FqT$ zma10Kv&$Bx6!sk^0(!I1^w`uthP$bm%s3wS^~b2O1Jvn|scP^*hbtq{O36&sbB~~f z5@p6m0M*`md1nLh=a4_9Z=%6Fl&n&5p2NwX$-t>bK92ade$u{;LY{iVORjK#+4wk- zQ}!kxf|)Ry{D)rca(Z?t>!Ykfk$&a6&W1y#ws->Z{ce`qUlDx@JVWeNJUW~P^lfbc zNxqcc?4E%4ec*=&wm|vi+p_C-?&G|)v7!w-m?%*J<0&X$=>vF(u6?R+J#3%?8qxPF|kIKEf(x33+h34oSxuX>UHw$slGY{<18xln6j*qei`y8A~yLSZ9C>^?x7>XxO+>Dy*v z6Sn%!J&veDa1`@imctf#<5@7)gyqDY8gC=6XRVGQOS|(`)&_{F$SDuKz_otK<)>TW zimn_mM|PAaPiR7poTzL>=pNuzTQqp#ntW>H3`3R8yEoKa-FV?-+sNdWj@rMuRDOd> z7U7M9ga{8*o#P)B%n@qU&C1;W+T&$7^PW!uku*fD zyd=b`pH_8#jfv=;yy6p%H6wG*zy1k>p5NQWSzrmvf}0XXbw@qtAjGiV(me-a2ZcMu z-txtcP`w7nRYVTWt^a**jZ%Dhux#{L!yah>Wf*F9r3td*c{@(nG2b^6%>Qya3cv(W z$Vd6pM6=>7NF$pi%D$&%8*)mvXOJAK+M)}!O_y5fFe{J8hA#19{`0~Xx}9&yG3J|Y#DrvVpMe4)}^zHU7&jX&kL>H?6QMZYd-m_vTbb= zV?j>5h!S<(qa^`jzZK=+W4j30kTE?v!Lu z-JBKy1~+g_G`%-wFRXVYzJ=?#zVOqAJI$sHK;~K3le@NBmFai{HS*PHg`~M=m%Z&1 zto$el=p9~R5O!HjXNm3W+mRwNrwZpA;aUY=w$-Aeo=OQaWuh6`d7rVFRg9BuROU5% z-g|Y`NVksgvNsqYHb@AXZs9Gm^{sf_VXev9PO~;}{`^F>!0?faLPqO+q?#v4mj7%S z-3;_y?zBDMJPpk~L4V@IZ>~c(%2Kv%6WWD5l`KUu3%75gYAKkBRvaK z$Uv(C=D0~$mzA?($BZ5bLEQTq^I`U1b*C5cl5XS95M>zI1UTLW3`AkAe+|aRsVtj>9wU9<@I0_>9&w6=z}%5*}WLxN%a`%V8Q8ahVKvluMyhknUax-VxQmt$0I zv}LDL?TNutqZ(&^mgFBU&e;dHz9BQq%%JWS@STBy8wTX=u#FnC4V!-o6^{YQi=|@D z-4G}PnD*p--E6>!tF@oH-w=vLxxnQ6M73f8&@h0^R<^n7IUB&S{J1Ls%?2PYK?lrT zKGi7C16&^N;Lp3H!Iq3niAuFkx&BuMmHn5Aa+dEhUdnKPaFxYBaEn{=;=r@3`Xj^( z-}5=_lCM$_)QoFH-P2PI=5eRfr?)O8Gs^93u&0Q=X}fC-IBSk)>RbXpXpr<$@9DT= zHpCu4+XQA*%dw3jp8zl4{LL@&fSTNu8x9fQ$NC<4g3Z*>bf;ql9JV>q`f;aV>-#O)9tq6NRs)~ML`HHu& zG6$FykIz5Yc?lwA4E8b>e(22&|#+1nu(+nXU6B9?v{(@yCz&57tp*v+WZTkQePZvK%(|R*or>wjjUE^)BMb09zr6feX5oLXz=^uSqY=w)rA$eQbeM1rczDQsDbakFYOQ41 zvlXjr*3)u6kMLdz%rW^J!kWmo{}r#B)BkhSy^sFgU+jVwz*KLIfHeG6N?u9S1!n}F zc=5q*(>X76w+#-ob>{;sFGs&n1h{M&dZ#1C^PUV{zMg|Q*1C>Lh3ijkIct2sLFpZQ zs6~l+b*E#9+FhVX=D%>97-f~u>=CQ>$VitiJwWcgf+WQ%5u$`c^#Z1=79ils$*Un7 z$RMNBm27`<>krhuwN}Z306vVPfO0nT;NY3StLrV}MP$`qU*99|ecT#itt;1eUTV10 zssXQlzeBs0>Co+GtY?nxFmyPc-FU~M|7Qz~=vw4 z5FZ%OM%!B$l~Q@x>|jYC9?O3nVwP!LIiwDc#nCx9Bck|lhFR;;5t$xLL;wY1l`yFL zeLyZEpc5&uRSh{*BAqdv*tgRD#IRg5n`JL!wNZg%#reO95woXyQwg^4zn5q3@D4V6 zckB2$80Wv~Ku8JXbDyMbwl-G)dP34$H;o*xL5xGt=D9*IL+F8HP~~) z1sHJtjGNW>*z#z^R9EzaJ9sIZZWpZCUAw62F|6&K6Mm@i;KN9=Y&X&#DKuAw;Oi7| z%GiL4qlRTgc|B=Q_5$5(PS1>o;;G1P4A`aat;n>{%lshs$ z?bJ);ud(71tEUX8q5yw;fvxqa@)dZ>GpT<=>gx5RNe9{Eq?j5n*`@@7rHY+O;*R;B zq^iwXq9!wlhk>UxfV)Jcx#*#AT>fpry9C0h0emppinIQ0VMCU^)>QIX*iPN#W#wor zl!V!DQJ^$t6-Hng*3yHj6n5SmMe1EGS;&-Rr;e;QEviX)16(~+kF$V10g%FKj&+fB zU;wo}DKJg^5L_SxygmHI%dk^_rc}GQsHzCZ>s*{{2)1XFP8v+niMhKT{0+!d|9;T#lQrt(~_%FpJX;&hw z+QQ&1!L`mQIe4j!yRN}TxFFD@f}f&`w9~Ah3xHGpy&{+EFCmOfzA=9GB)aQuRUP*W z&UFtR$O#}#^%SnMedb`mP2IRL-WZBkrqdC``UEHmpXdll(eDqMh)a&X$an25+0uGr z#={OdPnLveyAnNN0DC|7?fq}Bldq_|vWo?vUfrVXeGTVXA%%_;iTe}phvtztI396L z<9`2Wn`U@0VJ24ix;Ol*jO|o?N5dyevz#3KR-@8%bmD~5$&4Q zOejV1wk`#nn#8B?d+4fq|5-5rA64Q#`l>y1lXI??W2RXbjNkps&CT={xRE=0(h(OE zFK{yH>c15KWBxs837aikZau4D{zx|;ccm^b*Nc#$P35#P-?UBWgS^x|;ia=R^B z%g1+l;z#@O#E=sx`epf``{Mh&zAKcOAOf|zzI=-5f!iwgK*d~DdPz%R#;T0-ik&C~ z_`%z&Qwqk?1Up#v4J?JQ#&Ar%u%vK3sYh~r|rm2wf%MBCrxfwEOhjv$~ghQwgUYu4X~3c(ybw4-=f5h z&yih493fjCSS2}#6<@*2anc`f`G<{ph>4Pc`c~6tW3g55$PGdA8c3m_o^9aL_X`x! zWA6S<{QMEA>@5Q8Ts3xejcFoG@I&{}gBx*>b%?0>L&uY|QhF)$$4o!+~5yD#!c83Na;K6|_bEd9|AYv3H(S*4G z_S{87xbU|ZyHl+a!TPIrwVP$_N`?r5KnT69JW`Faj&jvCLwAL|HN@_bIXAfA5iAhMWrow)z*$OIOpFa`unmWEfTF##?K#!KX6}VtF9|t?*+q@b%}X* z=a;~(TC?dRUd6D`?ig)>bbL)*EVDL%p~#{dVLCH*V2kq|7BPeU{)<=~4*-MeU^{dF zK;RF81f%{sY&q(826rE9pI)~C-3>Y3yRVLX2Qf(r&VUlADN~QEpNPHXn2JEK%o9PN z3n?qlrvGxd2_`66iZAx!;&8=?beG|ICKfng)hQ_gY*-MBUM0L0bvYtk!E<ef$~etXmTJ!Yd+D(m5|w+P}+eL32L?6m60ZCuu<2w1L@qdJ$xTYI(AC2zRS z)83KR=hBF3J(vs#mb$&YB|>@eA7#n?Gz4NxHGJ!?4`5neJ^9P$y6@a)4U1ScM@ftW z(!b743&ymHZ2VXwSczqPz{f5NV%?i)Nz?}&G#K<_A1skUwXs+w_pwr9Bl5-z+=vIc zIn~G&tx{9Q(x<-j5TaCM+zbP?)#H-zUaF?q5M_LKOerApbw<;)fhErPGb-bx`KHI zKa}dne4}uRMH5Ml6FsSaZNH;uj#vKil3cKSRjUb<t^6^bZGzR>WNXmMo;ZbDcTmOeD51*VAoo z4Jm1-Oy0H3$6TY?hLvrJ*t51XQ4*>ZoD9O6K)Cj$5>L&CJMy64eupSB=#+i^z)1YB3uvBRb9B-S^-=_5?Z1ZTp%^fgxx#q0-`vyw&gj${+kZ~64u?vBTMY?~z5c|$to^bk!DK`V=Wuwii8 z@M5#5{j?ly!p?$c%tsxv*w;;1au|VS7n)luxY7BVn=Ta427Bv+yU}b^b=$vE4nT~o z`JT3BxsrIe!8*?A#HnSIhyV0Znm`RNiJ2ODR)3RyJ+~Xc7UNHIeK6^~#UV|WFh$|H z3NRZma>?=-lnpmJ72JKN2K!ciW>;Mhyx9|n*uV#w?iY+*Q{Td>~Q zIrv2f_sRJb>V?yEizd8srYBIfo}+f~wJ}JKET^G8rC^UVIl%d-@?96YaFamT(nH!Ara-W#_>TRS`#hLb=a#b&gqi)1%R38V?p`m6y38J*%8t3^v zxT9YTooLakcZ|Xaq*MAdyW~20x8iXRvgpQ|t@||Ze0LO0#oq6z=@@a$*V+5xzZNe% zVCdp~O<{);1CAoD7c4kG5j{QhFj3Gl{KcDXA?0pfH&Hv|7#C*C$L#(NImo_xo@vG< zm4JQd=CHO76jq#DMkrA8ncp5!t&rVz#~h_6Lzm;!^N8BMi~>jyLv@>^&^0T?xW;-z z(`OK}9Ma+9kvTVqJI~pZ_~I|>{OzLvQ|4B@GU2hIYEN%mS}11Mmb{=v`D9LZS~0u5&GxY>1tn>oJU86 zqq9^7*S)9FuOzzS)DWp{q5ua9d^H9?PrBUixA}_%L=TUYpo@oVJ6wxA#CL@TlTVT! z-3Y$GoekH+-$h`>9Ee2!*iiyiN>BVBOvfQN^m;2K-Pmi1S*Du2XOTNLQi%@YO8~v0 zZ^LJdqamUb+--p#_&m?7mx|WqWJ5diZv(5uVq=P;lw*?`??G_fB39F4j)PY{2H#BS zXh&WsK3<8Tu9Ch&yd8FJY?H_O`1)Vo#HM8}Ao>Z(A8qI76{uJ+fi%@O3^CVv)G~`a zPB#RTdl;vU2}|Oyhx<_oO+4mMJ5uCZ>Zl0E!71BAFM^e@M)jXcXLbbSuG>#}U0U3O zHGM`OHlas4MK=VI1C^bf>J-%8f;R&n z_ub^YH8mOLo1b5ZNIw=758T`nabl4Q4fq9q{lJIBm1)WKF?&h^GK`mSR3v%^&#$t@=5M(XyJ`$nKBD6hBY|g z(uXZ1HnnJL@*v~qsQArQuW0{~BNihDvP|tI!}{cS;|6+QgJHuFucTDRR!8{0&&~Vr z-`rS~V{PRsVEIiY6k;uXcxCSy5eY-=e(5j5{srI0q>Z~}x%w&?E>;+WRjJsET2WRk z>e7ENiw4%}v;-o-Zv_Kp00^dTV@H3?$8Nk4@cO1tsyZ%d;lQ7vD!bMY?Nzgh>qEY`AsU43S3ToS3&j%YGkO#9lK9_9c$9}oXxZx z^U=S%dL&mX=9W+$U<vC8~pVOR(m)NVVOQ&MBbKivjunbgmliiV$xxt{uBN!1~eFQt{Hhk2I=4i>EYOV#y4&6BxeP^cOAY`a;=e@FtK z|4E&`+*R3{u>Fy#rwdA%#+A?_Rcn!uL|08(qbL$-a^Uae*(86{T?WfhSbcyd3coF1hLOOkD0^t7nC(Sya3t%*o8;eDOVqlDS(c|9o z7nR&aF#W+_CZ(Bj$7NG3rQL=$!QvLSE6zNgt1j4uxWzgFmaSrZoqvo;%p5-sES)U~v|qUZU}BfHpYAEAC1zGt3L3MZR3l zkKfjYPObiv)3_5QU}<;1MGy^Ag5#;w?Zw?2%obdj70X(b4(J|YsBpzB8B8Zt&l>_z z2x5OXHOv>jPCaL^MVJ+W{shy0i+`Xe7L=td=05j57;lPyU?!_#7X8-Pb4uekW{E1hNr|y%hY!Uo;xBn zwr2Sl{;6p1cF-UN+vq#z9-s8ZayhIY*mIu#R=>Y8qHq3F98Phj8moB}r`WY;=(RlF z~-D9I5A{65_E=G1t@fOX?dbw~Zs^4@!%*(tFeWO#ExTO0oYr<Ub0YvtZ-gt zTT!0T3aZGda=}v83yioavMe>0kuk2^xL)2RFMiKDDhxNvzo+x3;57&|JPM4cQ`q!> z)WW~s1-G-P-S?$%SWRt9zea&EYFZCLo4)%dH95M2Q65zNRa1z1^^OScI%Iyl$wWnw zy{$B%!HNCU(+&V{#xciyg+0RmtuH}<1}2^d6iQ_rgG@~{r(|M2BTm98mP$7V)EWC= zw^lW^N(Cd)qlz*=Jl@D>3}_+MCjYU0`(A=0(|Hv|^8D^*&u1C0=)6ykfz@|l;`VD@ zU!p@|j>zI=3cf6~>^xTb>KblT(K{%{%K}@ipWb^nAC|K+;FZvLE zsZ`QXo8`FUZqrl5Kk&O$bxAIKrsHlTtW7(b34)~Ut$0auw0R*Wb z_$mll)zIWvJ;$bvRO69o4Z`e4jhya#tP#djVlFY|S)Li2TEWZ^;oOm8-Ua7Cxc(8B zo@*$P+jHBxLt9`+sFl4cgKeSE(&`T`#GN{pthLR3pDO1}(klc_GoL|9%2z}*4+S!6 zvkEGMFK}^zU$AWS!8E<++<)eb+kKOnKtSMid1J;TmvPC)iDp^0;9lM6A`Y*&Ovk3? zz7H!clIHxmA4*^~l0mu6mj)$Tq~t4YsW6HP4_21I$MG%eH}o@LEQfoEanwXuTI#F=kd z=(3%EvYq{BY(3I}Hiyq9x?qpgMxN**0H=ozl1LLV6M2~J*5fPzPqeP!(2OrUCY-Gp zjF>zs3V?3{n~taeW+v*}VL{Y@rvE=+_dnthkeB%`fDddmWJhxGoNsSgkgV~_KONnb z90G0Em)2T#mB`^Q7;RnVpB1{05+4YR|wNHie zZyoRGF8isK{8)GHFl@M^(-u!=`X#<`OQx^<+_;Xew0X*eYzwv*{GH(6;AA9c|*5tPI4O>7I1XP-Wh#dhDRC=TcTTziBkf3x#dT$9W zL{Pwns0auciV8>|bO=2Z0qJPy0YZ^Z2t6TIy_t*RFf4Fjm+_UaA zGizqnZ_S!<+Au&b3Sdo~Huks+|6bxhcS#8D(fzrMTH{_sLNBKQF9B=eoeXDE)g5(IXi-4g3YWtb-c;N0&T5{0l=ctb)peel}U-(aw&sv$?2sx z&vM0Bn54P9R9QZN>3XAemR1xN=NO8m3>nuAO$q*b$NyK_E|0bjX`o35nQ~a6w7vV} z2LsC!u$EVwYQ@ACv*A!r;1Ykuk7BgVqa@~rt~?otop4DAX1yeqp6?2jQ+Ae&ZS?u= zMlJsq2cKg@O@AE#C{urMm&-MZ#6{9>Zsjp^#0v4GOpsYo46EJYOTNh_uBA2zv%XuC zfT9(@X_>n8$5%XHzk5rEa}!xobO< zYE~W%@l-Bz8!5h2Lwcii{@WF)5dB{->+IZHcypIQnvU02nqLn5@gn_NIE z(F6|3LSpUpwRek+(7V)%)Sl!73l~&t}nIu+L^`TQz#aMRYMYu&QT+9BqP) zzwNr@ZH)eix1BS0jpznpjJd2W{JPtK{CjLw;!t_ftdmQT{A-S1b8dg9zUP1ybZton z8x5!&LHTCvbgTul5l$Y0wM$M8Zl&eNINLaU|K8hT_pjOvzhmofAMJx!f}}@Dx~j|{ z*E(9HuboAT4lf%bGNhhmzzW?27`o%*0JGt+-5&qpopInVYNp84J5|Ndw!_p=3Io&XB;$iEo-)kJNFSg;^aufpWi zD{Y^{kFna<;#dAS%&(&TpJx<2c@5Cql`@etT8|bnr%%fLams&%?k_dxF++|n@{V1* zC9}GC1%8wFHW;jSTngZg-#)n|CZHRK71IAief|Quw#UHww)H&KEV&1Oly2q;|EA~v zGc*3cetWZ@S0L@_R!uhPXB>a{!C&E)<4*vGBh=)&t$><)d=Blq{}z>a0e~%Sb3t#< zbJh~xJn8!jJotm52h6~~3w~eQf*20hhi-!xzfD#4BoLQeKb|lv4M^yj%vA8-qSE#p zkkF)$>aCbPqi#2Ua{kS~{sp!Dm4(J5QFP3l#O$)STnf5i~|LFa-YH-8Q;fXK_FkMm>6 zAAazwD1ROJk5f&)4y?nTy$=k80bMb8J$dEtQSsZV@Kdp%+4TK5UGnJ@&i^vv|Gd1^ zGXHfy4Twx6=fm!p{;9Y5-zKGZ3UDJ%EcqE+fYr^o;dSh9QR%GMD)DAc{`kP*05j&N zFMIwTTfkfKd95oDPx3?S&RzIhmh$t~d4=q*e#7vq6ujXlMee^v#oio{vs@1CPQJ`u zgx1OXyZ;_r#J6HeD-7RO*>tPF>4&>oGuI}QKG4gHxx{{J|j`^OFa&J+COhW^yA{o{uIoGt(F z+)(wVHA&?tzN9^nB=Y8@*quu9{gyXp@Sbj-q)NQUAv@&YP$h|xxv((oEcbL=;iYwB zCHbU$f6V!o*-CQlNqy1^Xk;h1=8nCGH1?WY!}d@5(yRT+cc0Ji3<0V>iR{+ltRd`tdiYO%@@J3z_TA^3 z00D3hngx-WRrk(xo5QOeKLb22b~VOHh6T}brj52Wr=KVGSN}wxQCr01Q?08rPEbI{ z5ANPJ0a-DCKyz92h+DMJ;i%`^9DeVo(?E;kGhaO;!baRmKlzDlmjTK7EPI&{2x7m2 zEDM6|^!**%CUBM&AaDp(Zvi3aqn`r*hr{K51_-QQnvcDamOUBBO9&MKahA}&8 z2+gyRJJZ8PNDqJ7zg+?D?fL`|5TAo=sgc#~3)>U_R^wgZ@wdlYWCcU86K^KrzceHM zt1BV6`OtBS$vwu!k*))D;a5*~Zks?nFF?R%=oX+xN-vtOZFdRj1LG$gdQf^@V-aKe zy@GqY4A8p=bgP!gRNeC);K1%f7q=_G<9bm5fy^wEeSjK;Uo8ErEAN-zsoeq|HwZc3 zYrmHnqZLoSx?Ki{Ydsusvsr6=XrQqOQ#-lmFX`>?#p|dK5Qz4X*is|uvjV^W*IyCl zjhn#Zw_Z&M0w&P&=8DQM6Z@--RI>q$FglBQ*xhk2lk3Kp9or_L%?=RAcRd5Bk#NJ0 zliOWF|2TnvoWMU$;2$UOj}!R6#RhJ@f|gVJ~wM zlaX$BJu(Ssy@;G?(2W~4Hpb67^u!r$FD?xOCl`!A235Glf(l{jpddA&Ri>-4tvT%o9z8fc>kOm8Ts>$oXK0zlzK9XG zGpn`7(}G7x$8#N>J1>rlmS=c%_>7Boe9lv}hW?dk;J4`GvuSW04OUudB41)1PEJ~T zdYTf@Q*P;+}mRj>iS6E>)o#8w4#OSno2vL z_?A?k_KVbz>)de4=aJtsNlM4 zq5N42NCpF>_&u5Am$zHGByZK6<%ncW6WSL2;P`_Ig>K>a;`IO+Dsv^Pl?R|+tFkJ8 zh63^~cMP0Z-FQCO%BqF(_#S7wJ)St~TTZ3Px{@c8I+`mE(v>CVHGt3(tAWWZHYx@d z!lZ?JsFQF%#3ibI^}mKm8`Mg%UupsTAso{y*d?Ut-~P%6|f zS8RMXZswYLxLUN~Hgw>NFbaI`G~r(@H-MIK267W&H1-X7w#QiaA{ysbCu< zECg#aae>hWY!6)Ta#|~p$D9UBp5jn{hw8}J8fDiySW>LdFu>mE@>O}jbd_1jz8DR0 za)U{tUH%paI{Q6p(ad>bemkg!knb7Gv}HkP$!CRKJYPO)-rFVfLGWy}!!`wWfwin$ zx0IynUzD|?vxvzWOvN6lA$;e|wAvp#Ot+N1eHQoRukJ8@{+*f`M<9)x(Nzv?n*A+y?Rf_f+r6XvD~<)>^yyR_=XRtV9M2DojTLjU>B|5d4FC4sTi$}y{3u53#m zf=AqBR}XEE#d;)QCp!n^ z9^PF;aGXAqc5pjJHhCBb=zC~~q^s#Xbm1MJuP2@b@gBias7!CShQME;YX|CRYg=K&Rr36KxLx!|v)U1Y4TU>S+yiKSk7{Mxj(bs)+|r$iVQs5n z0A+l5m-Qn+lNI^p^!BV(C7>Z?7A)+$0gQ6qK%xV{I-?UGsQhlq|2uyi?*THFB)hV& zTwj!D{UUyt6@ZJTPoLf1vRv3wH^xc5!vHa>BxApr5jXVe>6PH^SaQ7_T2LS5kgWb!!2O!p$4oL)XED7bN!2h~h{&TKf`GAa#7lwEz zfgqm!30RsPz=Jz)-Ud?j{yH7Cp)GZzT}zh!$E5tKk^h*KKMC2p{xKE-=JAM+v_e?fK6eFw$ z27PJrR;~8(LGy>@J?8D@2NK;Jj*S{$f05fOS9Z|SsHRLrE@HHZpQX50Vt>|Dt-@O1;p;oC zLm&F!yFQyJ+eP-nMfU6PJoh;9n_m5|pL{L=vuYQ37;Kv~LQ+#NTDk&2PrZ@CW@Zat zmv|PcM?bofgX>&7f;E_g;YZ={uSPY53z^gS{!~l%Sk}kof!8cUL(7RtA$7|9)V41{ zv=D5ZV+90hWM!K9T}ZvOtc?X0;0EvORj;hPYR2lzx9qYOJj!oz(YNa>zdS0HTooFQ zpC=VVI5_ZVarVfr+{)-sO|iSqn}#k5hWk_k$|^2zBVf@p*@^@+RSlAaLa-T8kRN*i zPy=J~1ATyWFmo{TRu%ikf|7eDaRAmyEJjrYa3AxeN$w3CnZ5}7j-onJ4$KQLhynVs zSThncc~FI(xg&?0JljLSjO2s{@+}J6oUjD5_ei_2(@`P0VD4j1;Kyt6wwb(HOAnYf z<|5*IB`j}m3v@mF9tf;z{JMuafnCbS7T|4jl5B=t!2W!TW<^#7CRmqrd=(7Ky?IOp zS}UyDzo2B;-z!dsCeG%gs+5Dqn)&E1xiFl7#zz#3yiD13j%k#jyTycMCf6YCojCR7 zH2Of6S`KO#ZMK?!?=c559|e5C_j^g6Yx83d*V!*iuFDh;*Rhg|D$=G4Qpw9}mq?HQ z#60~^YP3I6t<=kibA{g=ERBWIeQArWV%EZ2`2#2Nz(dc0!&^kG4{{bN3Q`*UHZD4J zBJ)SNU+*#UP)EXG1F?MYSmt78XAWvd((J-9=F^*_!n_9{94AH(^6y&b3;HOI^}h{% zC>jAUpEW35=e_05kvtVjW}|H2CaM}DTtd#9Jji}nUJ-me?GGxiX0?-uR}A_vBn%3w zMdn65^Es2~Vcl5r4XW;13fm!&sGoRkM{qEUPJhssgT-zy!v2q0f7wOjI_jx8|Ex9w zdK6wW7`J5L8J&m3BESc z!%s`6@h=F==66R34NW#g9w9!eiOOQn72lmHn0pN8^-;5kYUwWPD-Gnb|DQs6&l&tE z{AGzO^#0NSr-`Yi=G2j>6y*OOj2njv|h3(zzy5pmZ<2` zIexB}<7JY*a{5r4$7A={hq~ z)i8vQM<&=`)4Yy}5VY4z>^P;TO-S&wwdy-&3s>?T zkc6yGN5E89q~5Feya`xbNZ6x#$oyim8d_lmDQ9U!78KSM6s9c7e^;Yg#$1|vT^WrAzbRU26<$1A3GQabB_c&O+|njp18U z+9UY>XOThzu`npo6N{KUlg`-bWv5FPW#$sQ2$3%7%wy5Z0(hP1?>ya#!pJ!tfY_bDtkMT>4uokY;G8LE8?*Qrx@V7MDd`ku{o=B6xyS_U0lb! zaJ7PRVj=x>t`E(XASH;TrWilMMoWvVc`o`h8eA5Tkr2vmvem_-L$D{^H@9$cmWK0UC#EC;YIzOMwAC3c~06Ldb>f zlI+p{JhBV+KSY@>a2uR05-KRT(vbbxYolIkXe@MvF6ZN`zH=1p)I)2o9xysMup2fs zZQEZgCutT;dFbTBhM=K{iRg_dl1@cqIpTEYM5w%}rdz6oSt4^iAsc#m{s#;wgP9Fp z87#AgL9fEUqi*Qt>TS5og<$LFg387~WJcs1onx{!elq4gCVAxfR9oE4^|yKNc3v$C z8S>~!`Z408WVToC;!G@kZU8Y%4DrTB)Ltev-F30cJgZriIv-E7Jat(jsiyD zzxaKVx`=Zdj8wH-XPri!8-U!$KXpkTi%l+&KaJP1>!{)%j&n(`t-P^?W)7(WXr>B3 zfELZH6^JvSP@hsss8|tSW>1+&j~i5Ie)%ztOE^y)YqnqrgZaBb9$D)z8flLb4r)hK z?Q+L|g~<0@(3JmHTU9Xc>C53F>PM0PCTDN|pU$Q0xmuu}?n>5fIQOtml)>H;7SFjq zIUZ*1S|}e63w*RfIlwwrrDzh|FFliblsDt?#*_3E$Rn%;n*MDjDZVyY_lj>gm?|t> z)=6*_8Nb|tUC^lZ9~qE{EqyL^x6AEa;U)ldRZOcAD4k-anb1nGX?dr3Hdr4xdbnBX zFd05iXL>Fku_nODX$@Ou*Nf$4nMPmCG2 zoHFZ1-LgDjXOJUO-iUqWlKw&juTw9jX=i<-7}*}?PplBd>x9zYY@v>22mp12p9exm z+>D(f>I3NlsE!W!@4-d_V_Jf7Ou@4YIgcKG-45y|+>K9&A3VpgL$}BgyJDvUo9aj+ zUdR_0MD4i}1Z+U^fr1I>_c$H?o*&4)dY~<3fCI z$ckcZMA(!6I=@o$ChQWanU3ZXkP6bzs zhOu2N<9b0FPq3_;BNBROZK0BF@M)Cj(3hq0!B02GiGDOr;|B(z#SAEG8jt9}5DUjm z-FTUlk-*cM0rQl7u*J?h=}Xfn4R%|d+?g8?;(WeC@6KL~3Z*vkeg(3EKD`^WL2Kim zZN>0Zy`7o6NCM)B(o>p|G;*A+s8;yMv>x}f{WdCL`4y*;QHUv!FWN@G1-I9vAx%4O z+VG6Lg#@oYww)#0b>8;9qY{rgnp0|F#;QBsAi;Wmnj$d0PTG|&Uu z{_gCO2N|2ON3SCg_Iy=7_qtvKo2{8KuR)uZcGxVYmxa_58LJ>DyH!q^2lgTSPU|nY6l+x~5q*F@SYg74Z|f;@u~l+wn#P6y*Z{ zpa!er?_u>1O3s95d{x)gwXKs4?D1U7xw78h-M=oPFn?C(@LZZs;=2O3uJCg50=eZ~ z51f5zDxrg*y7Fc&*XQlPDe~~x>XDcb%9*K~!p0C7v42WK z#JIgM=ve=NXM>3V4?QCGOoSQ(_nFV{L+D!DH)~PnQl$wjtSdZJ<&+h#amP%#+KK3I z&^OJ68Bu0Vq$<<4=RGGPA~^ef0zBhuec6W^!j{~F%FrE=1OEB=b=d8^Aa1xI;(BfV z$KJ4@n?XtSuZSaC8lT9J#^#M$_SjgN*P&6- zPG)G2!n>-v#S+wrA_jRwMRU}rtQ%HkE+Hi;z|#=9L?|uo**)sT{O&}ek^Cx|lXKbb zQ+*!0CF`0<4$7);C$8qS2(AUGbcuqX3rj2VF3q3bCmB>`za>#>?oL;}(I&yGgEk+; z3MI5#9_pbrks{Rz2~A4^R1-L&uS!d|Po}UREVNdB6AONRZ;#AyO_1HR?C!NYndnn% zAh4u)4`cV!P5XYFor$Emd?@*eh*m|{<`EUL#yty1melj|sRL8-NtC>rR=nk|j7Kvo zi~YUcoJCUOe%~1>@4_ce1iJB;o8PE{Wn;rz{LalyNhdX}UGpq^F6>%@#mSgGTGDE7 z)GKzXy>w*L_3%pqG9CigRG~%6Gagu2;#uWQ1LCk9#Zh*Pn^yP?H;dazw+PBxe)aqJ zS0oxPN3|b1ORF~gR(T&`moBTdwZr0TWNSZ60*?6Xt~Bhn(|tHX>MxL*0?o0VL(A+u zGE0EqFVZ%+RZF9xaS=rcm~v${?hPcDRl2$ZNgtcj)2`%l*D-z39)C{;4|TtwY?oN> zJ{pK=-BpbvRtx{^TrH({tq-}eY3jlfy@wjPOA~oCO@ZKRlRT6Ca`<>|ZC)I5ET~m! zJv}!^_<`HuZ)1Y-nq>_=-?H;uOeYEFK*t9HCu~AG4<@Rtde< zl`8`?kE&G`j|Op1yz2<-k?ZYpQ!xp5(mw)R1^m}e=9Q7!Xt$~g`PTYi7Du}((8sea z)h|3UNl$HcBbMO7{;B<)T)$c1x052DBErAvFiA!^UKR*U16%pnl()r+hJ+Z4g|r`i zdk`~g+<|ws>liIk3tkdL@f)g)3NfKCo#T$|j>Db|KG*eDhd4I>O(C|f3xylT_t`EH z81|u|ke91MI-mjpB&eeUwl{ZGD486Y)Z+rR2xL48y;Z=nvq4&>+-I!NRtd4W;UI30Z&3KUk&xG zVDEMqR$&IFat}?H(nuAf`$-hpQKse8$F~z_D(tH8KWd!725=x+>zLM+)b)3*skmqN z0;Ts*%-lRFkfH-b`h;>xJd6epSb8$qft-mki8c!*j`2(7OyPqvHoR;p4_!U|3`S=3 zIcCk@erCy3+el)r8$;h>{lvQfj^Q%>t~Lh2kp7zQ`D(dremgL2jizNFI11uk9V1S=r(oli-vGxH5Wti_~iH zzz;8Wx7mUshnAP~WXR#`DU*1&iUGzqRF6Ya8N13AmzE^sz#}8lbT!PB3D#*y024TO zGjt>;Z_P8jt%Kb}uFJJ#T_096=EYF55bb+>wjglS7I|)o&{5@lqW-qpVj@1rYsI!> zuawWmFt%^4kgjqW+qO@YD5}~U6MN4#rz3X2n`SO@tDfL{(d#I_CvxaGrSTxHxUUsizfz94JOu(#YG3 z`(P5Ptx8)izpMg_*V#luyX*T&B8+2!=TN+6(*c)*A_K|2E`76Arlm>&>iSnqqe8o5 zFBp$xm)^fO>@%TPb$Rtz{VGtD?_)(oSZ{t%fu!!nz%w^Uxm%URcv8>Jlaa%SOuS7a z!@O`UHD0HL()|%eqb(tB0T%em&lb3G<@Fp>Zxa1(grrBMU*n+7!O8~osFxD4De(dY zt!H{UmS~gZs;2E4W~y!jWw^?vh4diLwSei@G}ku884nB5UWrlG35t%;GmCds`o&Jo zOXZhurx$mt7Wq^Rm;qZ}4rh~V5iX(~ugI#-ptWzg#J7qbF)pIT$gq537x^ZtB48=# zq33O3;2y)dbjmRxBjQWe$Q>X#r_#%CXml}ee4$a>WGKKQhy-!zn_r)OF?*El-7N%$ zDy;EY{$dR@yEW-zn_0#5QP@Y~XES>c7~Fqf8jSd^-!-KPhIsFL4K?8^^wI(C7#Ci2 zsEA#ka$+;Hj#BT`4r06aoG1hC{=^UJ`!IV!sq+KSQ$jP-bSH9e^5tY*sCVDh0cfv7 zi`HOQ_`p^4tsbRw=_bUZsTS5}7Pxhi<$vItvU_e$7)v?WIBe>K`xR}>f1?|B8m-Lk zZWldI$)sJ9=1zasi1NF6zy5zfeZ@O9A_LUR)Iu;x2f?o?pj{G^I6I=m2hw8LO|josADtNt3}*acq8XR$^^-DW=f{ih6$-xdAJhZfTc;hD+Jl7z)yxTDNCd0im;>{qK^x$z#ncXR=t?FBeGaE(vlu3CZXCsq`?;`TF9`HNaP z2zwk}U>foj-p~3xKMIwc7lTs|61+QyVZeXY=no* zIdEn=5@5rg+Sn*D!+YSAjNq8d+6gh=p3h&-Oyl8|VU)&)F*Xx}WN2M_v1}2RE5*o# zHQ1#ZOrXkWfx#?{t|wXhf`6qHw@+A;s1Os6;#2h`NHVLK27sLe=_P{Z;Fn;M!cG9(f<@{Vr5uO4| zZ}44w8bI_kQ5`sQ*=T^PUdkxP$0Tbxq_#SJtlIF}Z3iFSpD4}xeLh*>yn9y=FU#xO z4`IKGZ?@+OZ&jw628!*HKo3-NeKtzmGiqdpO-QM` z_n$IJ7&=#8VVxi>bJ&e-PulGAlj1tpx5A&l2R}T>uY%)(nRz~g#O)F#?&#bJdV;Cr znnYuejcoo>#Qublf=4W;0ylLMq=zMQo0V~Ab#ia@qW!o#jV4Vmfum1#3Oxq9cZpM> zBK=c3#G%P!rDNP%;g+5Xj!&0at3F^`wenJpNe&S^_+`zZI;Sp~jVAyJouDi?wL5ue^#wDI0%MX`=8 zq+lg(Y)_Aac6x0~BX^mBoYasecVFP$9d{WMqJ3!uYNkimVR?1jWveo6{7=1>HBN&6 zj<35CABH$c7%|=>^Gx}!6zFm-zpO>D?+i8r4E4vQAupP4 zy=Fg{xywGm#USH;#-s+#iB2KH5^C$BoXs`x>9dtN6|tOB4pXJ#H^m?N0q}r@tf449 zEG>6t1ET2K9lutkpU(9vQ_K>+6dJb!8Gl>3GP+#{Wx>U9JB^UX` z_~P{^@_z(X-G78-$MS6(6h8gCp@Vy%txz!zEjag&#=vE~9oY&U&#DA_r_njEtqPfvq&k zC#VuXLyAe%qv6RJ;xKXV&(9h_=nu)_FYC2y1`^QUp-127pJQUt`fi8i`Wub+1Z}oz zF~kQQ6}2G>hG1e6TK%K(Ufrp9`TiD7Q9*mvcPg45cl}GjuM8i`$$%FFCPRA*VsG`% z3E-Qb}EHT}sAm32GH}W_PsJ;h_e$b5q9G1_LCM(J`5bB9-sb zWrMVMpM=00E!;;LnxlPQ0!#HE6l0w$n zgG)b-up#*N;BCPlyyOz(;XPF(w6Ad3Q!ICnara&1jzjTO%cr5FqWwpujlfP3*bW)f zz0BDvo!oCvkKo8LWGFtNik0+YS4msJAbA+r`Z9dtYA=AKn{ zxSJy-P&>&9J~jP=PZn-B`$Zf)L9jcqj^;0k=#@uNg{Yc!u~g)p+ak)$5#r4~&2NfLX)~B*akj(qo^s>bONcO_a z+u`u73%-0R@wokO1lB)h&dl*<5FP!Z)923qlDDykxLmCXPH2HcfcImNnCHyq@@QSMhnV zzS&3F>~!O3lhEts+zXy&v|?NJPwaIAUKuhXuZnoM8*aC*O9* z{uWZ~Wm>@cK0zkQT9mfvGXC`�S$B(kU5p`&`PdT;be1B}OmWBUY!<=Uyi8W4 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'qlOS9}IB)55xc8pud9(LB9U%G{uJz zB7)}Vla_Cm5Ub`PT2{!~u*(J;CUQa=8}PXGRz zuH)zEh~&^lar7KSJx-Ke(OElpKF{w zNcx^^er92zr>kp~&79!y^;tt1jQGkew4$BIGavT64neqcuF@>g?4LoW62&EBtOc0)^#gh zaH)6z58;6F_O+ofnfnML5zkhb(ofFIeaGs!P)5enVShyQjs5UH*xu9|CkS^;zq0!H zM^xXOHmm=Ls{C8CgV_G2S!v#5V&*-v!ovWz=dbI2s_Bmdbi#{$p#i)FZj}P?LZn3-pksx?J>BqG12^xf{-cum42P9 zFQ|BPAZU^|L z42=xieb~+aDHXp;{^j+AS3$%~BH6#(BZ$GlujCiP>ca5{AKitJGk_*ORyyn(E-R4J zVHQN1C$CVAt02a{oL(N8rbITf6T+{om0Q7+f182vMogR7ds*+MV=q+;hLCepuCtNy zY-m*C8}pZ~3yW0{Aprw-d~{Ome|3{b!8db}|M`}Qk~cvV3v?^<3T|TlvzvYjY$+g& zHh5?T(>p1p;^};Vr)^03fPRnYT2m2Uc1eZR7QXXP(T7-#X(4;Q#f7g{9{&VKPLSNp z5CiCqH%Gi!5ywvT1ZW!}U3fn<6|v2Gj$V0anKk(_Z&N}BLC0y(7)A^;rH*~@d*hv<;>h?o`%kje&z@; znYpf^_gjMyqCXnb{z3wh|DtDQcr54!&9SE+H{msTpZMIeL314n&a&)pHR!&FxJd zhQt#mbsqf;@rc>0mprd!m%7GqcpiPU{D;3A5y9ZOBMnb+3gc9eB}AH%3#i;6R$9q;*0s$4*k zfd<(;fpieFMNy1TK-9rxgT z8UAtdACuJojfv*G5%!#AZ~yuu5z#ZmREAtK?lJ0_XMpL8C7~LZ(h}PKI`S>a*Y(aR zs!KJlv7ht{9EI6r)ISVHA|%ZLMDo7MOTSD6W-9x2N!kaM3Hkji033N47ODp=2=(e$ z%)mb>iKmx_fUb3ufX}nPL?f7E-1=0)Ie*lho!xE`8K#@XmMw%`)8uKM)&CWf&vSrg z!Q4-Oh@X8~0W*uTsYX42G|WW3kfs-irQki!d8G7Wsln_&u1`ozm9jy3d5rzT!$y!! z_4m(zwC-4FjSg+QIBx9NzoTYBm`N3}j-zfIQ($$3RNBXs?~4N7JILQYB21sQHb+{7 zUU)AnlftnIm|0ZPV*1;_;F74mRQ5X7LDv1#`>W*PQbsY5ubMM)RFl_w2?*{Zm09v#OzI zPV3E;LQ~|_Dq?ub`Nj8@eikwpzR4&KB3Z~~Ak92tV>RX^B{wHzSh|ZLXQ-G^+3q78 zTN9tRRxiY;w~XylMxOdhZ092%*$aJ-L*f~(i&)rz&JHnC?~>Ol)UMZK*;rCjp(RH4 zT>pt4{x2RYn*cc^BqTm1rJ=kWVZZp;-F6m@(AJ&9q)c(+c;VFnY7dBH6%Jj7vh211 z3?e7!Uf~~4AVPTgXwdJ!|L zvat?2>MpkTcFAuLSf7)!go6{saYe^oUD3pv*rb||H~>Sbx1gz1m&!nL>rb22a;%1i z<{*yo;!r#ZB34fMj;BBT)bI_bNTCA4sKckWqtT0n8S(XsC)!siVzK%EpOpJwd}=ld?ruck&F4G0zP=Nj zJBs!6g%~`DTBANJHr)Eg7+y3up50_R4#SQd{Zi%;e?NFdPr(u48X#ZR;6Iidj;QWE zWI8N~_@z8cu;xq-^ z>f)2KIn5KaT@Z8o?YGCxOP>JZJLyDXL@zi%7mjQs^*mG%@3ehRGP-#~o>7_~3Fp83 zA`q?6o()mu-<>RmG;$Fa0krLoAQn4rP}bIs#leZiBAza`wt9kc zCo0uZ**{)gU#n)>5(IzdZC@!HzTG)1nhY!Zs^1uELx#b&C{ds<188(;q+dD|nx&O$Lqku_h3G-f zVdE$+7Mi3J(a)H7s)rT5f%qAX*9gkU@D%4r@wD1MI zE8iSaQN)k5zaRzpVkil#dsFo1EwZ-iMmvKni4->Y1$YD)3txGTyN-|*f)TR?*VM6h$%%k{F%}>w zp6Fe(rb^T-F)sU>(E99-xy_@A>OVTe)|`oM4{JuD7Pj$D@-IFt83>kt2HGt_oY`j$f>$L!=F2U7D|` zAzl|E^WO#9ruwfme;fO_H_Ve7OlWf|d#FSk8*j%-*@se?d`m4Iy|K1dX{1xQ4y^|p+j0IvkRE4m`|yH zZqUCDT-j~#Z5>^?xIUC3Ff>K*#@`yY}OsQsvh>RqE2H!J+kA%z^D_=QbC{ zDDWBGL}lJ3h{$*39~}?c45(wX&(>z;$P)oERWND%y_Bc2%DvlXrzs&A6M)Aex3Z3Oc;q1LO7~cZV;#vo_iW%K#A?GaVhPy|~$9*GlT^ zHMVFjO%Eo8Ek{y8hp!P=(1~(YKBzgghCtnJKOP*XI6|FOetKL|gbxsyyv42Jk~a~R z-~di!di0MDBw`2F_Xnj_8IR-=KLt$s$5_u<1nl8~NP3^u9z zF8%EbDtBY~N(EI#FDJ4zHhHP=_a}wFBQNSBA;Sr24EN9NBp#`@JPBbKb9QhxWwYmC z>7Rhx?ukYu$K4^jg?C8r{yx|L^$pDBVe75UOlrzq$a9<`a#VQ+M?=z~Ug(p_q#cs} zWB1J(FCAQj7wE+FPt_NeLssZE?`|ap1)nP@C}g~y7+?Orc~Ug+2=^>nL)g8Xq`C-B z)*Mexzt~-9oALWq_IekeOL`BA0~x@$1I^V8@ALy31>d z(Oz9Nh*(Lyy!CF62_?%p)E z;INe3TWqb?BSzi0Q1_?nuxX~3_C+OB$aZ+86X z&dAIR`Mz44d3pP5B1uMsIS=GGbYLc4?zmT_Vw$fl*}uZlkSo9rQiXtyVCfLssvS$7 z#qzrL>zkYYsjlyCtF4^b>v+I3oXyP<#8i&F=V9$-@NGA1Hd@ON zOQ9;Wh^pW-u0o$tBo5B<_{$+~pn6>eOTWNJyQJc}GlPTDs}?UK2Sqq?oP;w*Q8)Ru zGCR>%%v=Yu3Z8LRybRUMeqQd$GYQLO%j)yuqqCKX_Ny5~beN3iapux>FDuYLpVfOh993tEw* zu8w@?YLdJ6*vxq##prrh;laeX9CJmcB;S$V2R%8oy@HIyRCdcx-iR#4HX8fy$^Lck z#03<$)KsZER`Hr9R#=#bwXKRXjI_CnS8eY9d~K(i*VX1glh8k43+^fS4?iN_yAEWI z9Ix~KRMI_aiUiM*dgaxQ?|0E?e~4zbA5a^M4Nm5FGXk$7_EJO_m&1G#OhDbE7Z>Bk zHV*XuEGjN8-{uhfEQ6TppVC2Q@|wAyBCWWM_O^Pe@KoOGe4*EX=#M1YonE_4ZyI?7 zIDU*y2v=OM46JIP;hOV|I<~{yot5QXEEEr&?z3_Z;}W;L`#E{Zm8@cz=P`r6(J2NK z;oY*Vp^eE3@noL?vBJ)xxpZHSAfJgxvg8-&c!<#@wg!Kjj@)T)uNj!UcP_iDsgJ$c zw%T%BlH(R*p4R!a{MI(W=l?vlekyE?JH5)(oe>(Ckw6=M>((3fCxoWBsj!n{` z-asgSw7#7B?VEQj^Cs(onPK?*#&HMZ5c7m_LZJib!8`NX;sRxzbZw6l&XnRLSyC5 zUJu22y5SGRu8Q-?_%roHi%Cd$6JBn+`U~oQQI${Hk;yZ;kiE}BGOAJ8LCWmeaQ>}*ihBBnNzc|(5o21EZ9K*BkmL0No9n=g687e z=bHyDw>1|x+X`6_H%G5N$e*=;LxsK)EX z%^y%mv`#%yUPu3CFMl>Gu9gm*SoMD@7UUOYv^I1|vurJLl`7_9V?yv|DqCa6RNnV{ z0nSKeZ8PI2dkwEvK3zDj_K7=KeG{Y2NE=-max3BDOM1L&+0vVIDB+((~E%RF0*r8sW=9&eH=4F|E^P7X*-`(%}Y31T+@nqP?FBYud z9_8m)-|e}sOz~Am3;Vo!%gHq>aS7wr_q;2D#g-^S1cH9~sfIbP8gqg$h=~ux6X7Ia zjGe&q1r~$r`M(zsIRI`o zI;!m6s1*60updAkm-!)q;k|zd04-!~Y6ppu^_QjOyh8#BMZ$b0p8clqmH6I8r0)Pv z{;SD&kQU$c=;IFy-4k$?+RCU(sdTxx*>Fyse=DCD6J%LPY@l=f|;&t>UDcm9MKFSiFK8|00XX*4{^gzO!5of%t9x&y6sN?qqw1k@9e?jI1yGyc&;Q zCWM$_%qDa+uN`402uFa%<|mHHqiUhYH<;OzOdqW(=|=d69q1iG&(@ekx~+0y(;a>X zrnvMILtg$nozc&><*gl?qg*eqbx<`%NpRNJ3IE^?G8tH}sA6qzqfltJEGf3Gx_C9^dP zYMtXRnmkOO`f(6=4ooYIrJL zyUCZ$Ok57GyNESEYXClpVl$R=RD>@S{~Y9^T}|w7Vk{XcjpMdX4mw#5(x>#eQT^HW zIws|*^{tg~S&Q0#otp~{>pdE$o<2SX0e2}7N~=Ii!SQ)Uy4spb9)*{{exjaEfvC+L z&}qsE&t8gZaBHal{;eEDMOw(L`oM66a_8#s zxOeG|nUT1?yX0~G)6>Hja8f^I!_pt>L@aKRGAOcb;0d+>(Z%Vp!CwFXn6nxvUqK3l zX1Q#DzFgu&o4nm_bvAwf)b?Z_MG1%-#6GtGs~1WKm{d$9rzlJE{=_2vpoOLb3hWiY zXP8=#08Y&cG)3G?Ny~B=13vl5uK|QpQ84yhI<}G&(0XJf*_f8)<1%nZ4E8-AV1mM; zw&k5EUSWn-CXs2t_mhuxXD34;*d2xD+Cn?x zFSKg-*;LPKgjmFceg~#;6$YbVh+{i?nW-2^`Z%jaD2q7cK69=9WWu>VQjQtw?;S&# zm7W|lj@2;~AggbHGqoHR&K;^T`E{hsEu}(Y;AdXymsGVo3a!D#u zdpAEie$`VJy11Fs&84@<8(mz$u7v0tRGdvU{?#{Jjg(lfIhNf$8$!c9J@7zT5%!Np zg-E<&R5Jw>uyoRd^{|wpNupgekin=w1(f5lmx}S5N}Ch8wf;g1P7&D7;SO;H^@n>8TX zvZQgtYe$?5&ePaSF>8Dnu3oKlc2_?JkF>I};Xe&R?^#F@?S%Cd%E2#2W7h}zr&~u0 zbQ1z2>*9Yknc~h&abKuE|8B9{GS`&jR0s@|x(|=qJTWA%0L}tBSNp<3pd8}PlfzXA zs7O`HRZ#L%1#e0>QU(HPz4>w0~26vMyXnP-k|;Bx8C4I z1^n9v^Ya)zlQ!C3xGhr%|2!#ZrIIL?jK%-~W zZ#EDb{*S=T?;(9WA3grv`25wC0- z5$fu~l9`NtJIl%}i}|pE^>n|sA|-?(#0~h(#Kg35!8DhDiRGq`y zmH@O33=Ej{k2HVGM=&j{+dHod)=qZzTBm)hcxmb-X+0O8ppTDo(URWCcrKe8#=q~m z)&RGupEzFOos=Yj{jLQ#(Djt(ZYG8`yf0v+C|}RQm|qe(qo4%62o8y!PCC~zZ<_*O zE~)UlqSX9&Q<0BMM?LG(HWYoo>rZfoND5;VV3P}QDokI=5Ol66 zu5$8|Q)p~ol9g@e``&=hnPT|T-f@aUVC%}l_Y>hp>)+cFcGHL>=_qhZ+%}QVX49v0 zlePB$rYaem;QM31@zv<39lbgPnJ1-*y2iI;lVa!ej@2UTS@8imoz#av4?2=uhLWh` zcvz3uIMD;%1s7yRT2yJ7g7_zxCqx^C@ToNX2R%oaS4Yok6%HofE(%F8F)XgF?&Vr0 z|Fsp$sZM}f$v=T9_MT|mr#I{2^f3}#kAMJ8u#J$vT=Z2^d`?Ckrh!5<**=UqdgPCs zw`2bmEL)t%xA&X>*bcFd)8kxMRPEBA7hwHwG!8$L;vWEz(M$>RjcTvYh}9}juS39x zY!-0&ib&MFvXhy0!L#di`a93Z-~KLS!+Tt2^(bT?lW%({0BP(|=veWH%wHp0C(Rzg zW$#(9lmOHJIiViA=}};`N!9zMpe=0W0Dm+Sqqucy8JA}Fx9tHRb*!{6wyVtX2VW`S z_2nVL^BE?2^U?^hVmy?c)_#1(ADIb+eB@y7-g+JQn?sP?1IUiawx;#^P{BR^VN5kX zu>EJr7dZm4j@72|04!hY?J@csuz|g&zhly_!NGwMQ&bI~C=-P2R*H_|YfrU5d+$qE zoZL;v-Z}Mtb)IB$QA}9fzc?<^ldcHjFKZ_ij|&w*qYu?lj~BDuv-6A*`)PtXNlc8* z@Eua+MgU)nIpMt#_7l-1%b4n^r@g&B9eRm>;8HDpOSQW4L~_(Ly`U>ky|gaCEz&{Ctf{V}mu8 zj6A>{+QOG*p%2}0a+N*0?jtZUlze~kDPQ=-bXyDO+Df{2tc?B8tofwS#KJU2Uw|Gu zzo6U9=5O_lRv-kyzI$uE%oU){FR0;fLo?NW+2PjZfNrA-NL^hNn?Fkd)ZsI*VxYq& zH8nc&rV+mUss5lab~SbK_XnNBplwX2Y8p&m_#?itSnlpcEM1RtvCiH`I+pc9IVPeD zbc#F{+4tFXDOJnMCfT)A70vxHLmDH_b|@WwfzRmra`c5bU9nVF5e}x*NyVz=`I^hv z8&A6|$b(YvHjW}Y_`^XWwNoPN9=xhJmU7OYi{@LoV6^O-0e`Oer1G#PxXpCw=Uo=@ z0;z>H<+2bUh-9!&m@7* zMo7E-P}9pZu$FFb8O zODx?G=6%512lqf9bvpps+;Vz1>~M?hxykTm7(y+z_(w-kULMWzoz zJ^QnAcr~0kx_EHH0Qy&z1Ctw`X2W2dH@)4g8F6yYV6-T=EyeZ~Th?2A!BC^kqxc+y z4>XVi(T=m(zwO|3>0;l!kjvE{(z}J(b}JgMoTsr15IRcjeginAZ|`$swd;7_MAx(8 zRe8na(8x&U%m_=BAEC~tC(cnaQqIjKqjxj9u;pc}Z zCn@LW=X%vO4vPqZsTtTLLCA62A|dH`WnA_wyj-Jq`TFV0)BX)EKrCdO%GK%1Upk>+ z<8}Iu6GiW;rvIrppk8{N;URp8gqa!%e*Rk3=@$#j4^90UmgSaL;5 zv8LXG89&T@KG_X^fs)pY1oL@BqFD`Q&7wtlh2EHR=2ci!J0}jFO(gAX5<$U7C5l^l zdm%f#&(GTd)N4rVM27IK9>zPiW1o`a%iy7`u)tJu?^o6wF*;Uf0r;c+7rilkc`_pC z=`4xOq(N(ZNcPQJ*^O??9kci+c3gzVjKJ-xJg=+np#E>p&Ry&LjT69R@K41kN4M$GZV#|_1_m8Nw|zWG zT$T-!7@q!=SoAwbA)PmWC^7|8d7GN%35$qerEq;(33?-OcKmKq$-lNqLj~JaXTg5j z-Fh@j#NW)%{pCj5nF<3x+cPXDn0hAkqwKHjX!c8LETxO|#3;Jx7PnbK zJiPF4i!6&}N8fVt3D?8pECA4w`0RZW>Y~CmKBGP048T50oRfpQlUMH-Ro|^@oYt)TGe2e33TXJ6gL13BM0)Fdb+ty1wtpaCudkoc^h7NbKbUmApDc^7NPfKy}{q0h57Hx z!N?oA%sx0p6>Yv<{d)ukgWQN^YKwJbaZek*eV4y?szQK81n@z!#-(C#ZtDP3zo(>a zwDB;@$@FO#=ToBkF@W=ZUW@Y6(a`7KtI;1lw*byD8S&?i?;AN&-hf+>(22f2u2uc+ zSx+1(cT2EZF3dk(yDex6WVYA9oWN4{Dy1>-KD2DceC-0vP3~ zi3XQNY({9|=7~-a6jcQaJ`r)-RY-;sQw^sH4RIF8%+I2*)d&Ya(Go| za*Cus29!yS%D4fpDpVq!c-JKqLJ*?KKVUk#e*Wb-Wc|c<=qXQc3`k)9hc@|MA>Ier z@axOizC~+Hc-ZzH+#nEI)mf=3d-RQI>Yd34Q4r7?8ilw~1FX+9$^5 zy7G1C?s-p}*mNAgWSG^~77yjvrG=o=G!_h-78Ywq+|O(r z+5_qINgGDE@v*Y9l3Fz*`lmeDdPL;E%0BRuRS zmSD~niPDRtts|M|rch1vl>h}R-^IeaCmGk#>Me8~Z-a*z5r`^N>`-SAg$Ywu82Q&& zw5i!UErR1ILX-}o;OK7$ZY44I>U;q^p0@;#eh)3Lhw3Am(=l(RQPZyhLu!355zPA0 zqcft{b6uB6!&QrrJ51m|Cqhb9XLA@SvYYUgB5o6+J@*)m?IKP~Zl3Vz2aXD@j@}j* zLcXa^B$)2KJugeNeoM`w7I4PK!3eYmiUKui^I`>oAflrU1`Ut?-SgQy> ze1I&0+4?E=zG8nCqeQeDxV8YlXVlj!W}s>{56^)igx{-E^A_m|T}6c@HH(Rnf-3Ax zW*%h}d#$3IgWHIRfc188duwZJ!f55{(Sv=oyjY|^P?%phpKrhV4RKNx;}=Em3^FC`;c zkzQ(Ukv@{46|vm^3PGk>-TSPe~YRmQK7)TL=%p_<6Lx zsW!myW7m{aVEe1U8>|lvZkL!BsZpYS2n8Fd?gpv7J!A7yC-Kv<_CGS#;Qf`H@?M3T zX&kTZ6=XseOmRHuAm4%2)#sKb_Ajj3#@P{9d&5^zfk5oOp&^`1SCqjG4MaGIl~oZE zPl~_u14`$rn5GfB7G-ic-W1Swe4%so896Wa-;Q&&z^${A4O5!}|2_-=XB`s|zjw)x zwW;(}?g=Zh^pQ3RRMAz8CrP$YQU6%7wPle?qoy&U@=zW?NtsD_a@fk8x6017%6r$X z4+>6`oiqW3(n0s^kc#Ah0Nf+nG2*p~LqX@IgV-c!7B*FnK{n*E5E@_o?WOmvNlnM7 z?oT6WwqnhkxY?BerTZ|puDB$wq-hbC%RST0mquWyp^K~ItXQfL8|K=ovV8oj@A#Wp z8~frcd1rV)%#qxs=VqEUUvNjNBj#JWb_S9Yr%=zL@%lUFT{q}x5{sUE?u`{}+ZzTZ zk^esFM4$hwSAx!G!Ct=2EE`wKm?J+r_p#FOZ$G(yWc`+tCX;&jMToCebHlfWw>jX< zw@g(7Z74gArM?yi=+VU7)CwZPr;G3UHn-;Xx{6{&6Rx=4==bsP8&0EdH~D1*-&&7- zxsw4N#<$AzqjpyaDqdwfVU;=ha{30MC^i*o`X{fQScv5wf8COJ)}0g|2Ai~bHpYjO zbtoEcF;~OP!}2OqG{vHrOa=IXR(^dhR_d>=z)N-)>DV5SYy4gU;CJAmYB1vBkA+-Z zWK9tMHIUj;T7+XVpJ@YK`2^+b(da-j;ExG`dwg=7@z9)a@n%K0mYynWaK}ks-)DW7 z2fbha($ma2`f1yvgP~IS$OJzkc29>KVR%KLHIjAH&B&%C+ik2K)Yga?=U>8fs+V6J zS<1Vf4qaGI8**Zx4V|mk4{oqRW9dBBUunWOXiZt(3yD`4Ba(!q$D3l2^782Nm2di9pOx(j?!woYx8=l z+si$nz5^`z@oAIGaeyMz1`BrF`{lUCO))zBG&clV#>NL&lc{Z)!BNgtJ)V#cw?baX zLivp@UVPzvv-X`MCzb{>*EoAl(Yv{<6N)%_5b*!7+!MC#9e2gTWLTfULt8bWXl65IkbPS6l6K!8ASf(2{b zX&e%q5F~hTcbCS3yL;pAE=@C?@7^<0H8pe3sd}rZ{Qx zD$^_uw{1CEr^&=6t;O4aB{sB?U8?;#7qjALW z&cN=DvR9u=B}LAKnh^+fvZn*kQ2dxf(L-~ubW<)}a0jQ7W*fSM^)S`7lDQu6-@H?E zNFN8eTk3J@TmFQvI5ePe$)tH>Pv*1~y(1?-sL+qiK`v4X_`%vAnA82@GGC;!=9_6* zd$&WmgcjVD)socO13nCeyA{4JkLjiMLf!&$mABm&rHXlJ*)28DO-@d(`vqDOwy)lY z=o+Ukm9>;0`;4f`TOC$rY-CB4U^9fX&-ywx=Tb^R((tl# zDW?Jb{>$zFheNbdU)$5pRO|N%@Bf;{O!QgUt;Da;c&nN(E-(;|$YT4JqvrR-GG!on zBYLJweZ7?s-yZ;1BXNz%=r+r-KzOUnXDp*_90iB=7F<5z{5OY$ix)HrISco>&5?VL z5B-L-Qyd*#BM!?RJD4#N+FYD~{k`)y|!mXqli8~<>-(;IQEqqB>y zQ4@pvEc`4+L=t5_4yA6&dWY>F*RB`Q7Fhiocdy+v!*pep9tn6bm-`=8tHu>sEVnt zzGu>i0XKR7@GuS~XXy!VX9qxt!|E}kB2axa*7gtDJ(;f+(N~oks)Ui?3%l(_rza0A zBUCq^u~8YgAAazWsJZnr;$9Vz+Lbo54d%2}O##}SkgbkkgNSEa?7E#B7w#yA38|M1|VG+CZ`ETP0T5WF?Y-@ei0j*B7 zM-;QSm5$*w~Sa ztcf`KtzG8}-@-vK-2PXg-X(`PxV|a1!qXDQb^j*@~-JWYU5%AMP=fVX| zmQXyJ_=N>>7D)qrkeXTM&Q}u?-UQ6`G$tGGlm873CAPu-$RdR2uX1ho)8+5o-0Be` z@;e2EOWE1k$}_c%OU2Xrnw+V4TT~+juqEiUC3>uWw3tXcXYf_ ze2M%c0GE6CAhlsFOw74eRn)Y4;x_bTshPVnSTp_QErm5aK~CKFF0RIBYjAzQt2B^MHp~igN>0`_*c2&OmOyok1<=AZ?R|h zzt*l2%L9{m7(*B(n3;^sSA)K6;>y0nH2&s(s&7KS)Tkk)p033q?1amP<)XU2&x2lRa;jm;;0F#(se*EJx zb&J8=rgazvf_95c`8_ulbjm<7x^@e>8{Iacr_9cwQ_XY{}XhOuE- z%9AF4IHdGz1sKdcBj8_Nnpy#~=-Rb7H^8>=#cbQ5aJ2|2)UoUR=EThz@(x2}NDI)_ zs2exSAi!zw)5~r1{<1jw2Dq>p^}-o_ENd>HX5&VO%qlM9r*SF`hvWs^sn!GX#b6hC z0NXGD@~q&Z$A%eo<(3yDLz$&pmpF`i5&%MR+b}srK;tdG@b622Pr#$=Ogtt99zDmw za4Z1IONudWEPrgcRj4&k!|np>;^s;C0OgCp_Km1_Wwnh|EE7T_6U8XzEQ|E$sRGi z9Wgut+@{d0ici!Wua|8@n%6^NNnKbtDt^ zW2h=t*?*!ZY6V#TV93wYIvhbw&e-F%gE+HZMKqcf*nSe-pq{^YV4nGyZe3cg_=-jh ztfkv_yR*J`90zL>iwhA>1o+Zn+R~D_u z#}(h?Wbm2)V^pRfLyIK0JGGC_syxzN9z#~7u@wd}jP5{jgxSwj>2-uRGQi*+4l*q5 z$H0)VN6Bio`q~1O#!5r%U&yRj@%lWrHBRjVgW1D%_~2e#5H zc+-4|=x!0zSz(m|s)4j(w!kxNj?gr>dTx+ZLP zcJNSuSeG3z1)js{4i$Gn$aoQ;Z5dzuB2<#n}tANh}hDck>o|B%ESA!UdG#S zfrW|rS5Vj<<{mjEk6m9+)=i&pAtxKJ?O4;DE{iH#=>ZirAKowrRV&aiGUfVlod|!h z?bmy!<5dq!u|V{l^4M=I5e!!;PISiGw?M}d3r_8C*vV#OLTghQX|Z-_2R8Xg!o{0P z5cPit85s5c<$0a=Bl#mDR0p?as1a_@So!EToxyzi=H})@qkMdPjC4t~d_L8@f<#Bf z)fRd0%{@ZcbJ&xPFs3+hQ4Q}zMDCNwJkjO0Hs@cGmZNU9r?avQjOsRdD4py{4p}T} zWjL4IWgl>_$u!|cXW{?O&Xz_)M+bzfAU1sPPxh;-JV&KYz*ORq2zS8?m1bpQRiaJ; zejP^VAl^Up2f=>BY*^x%0Z7 zw=kX9csp;2m>fSn&=~5Xg83%+awtBqXBo^0O3kv11S}N$LyiQ?`lH*fRM#mif4*XL zEi6Q>(Vu*6HBV-unDk6fgbsU!PzM9aNQUOQzachk`LVIyG;W1;w034TH#RyQtX=jT zCYv_AtjWwd)yi<0P2|RSJ2*>!8Sczf1sS~bkeyG!*!Lbg;k%lNuIq(cdvmx-R?ff8 z$PpCyb|BsXz4;-50#AR6b5^e3`1AppnvQCuyAOM4_j%=)42k4K2Fc9Bhv)zZz+(*q z2XC%KRiWzD$+z8|z+?(ZL;H68$qYXp*Uyq(f=m|e6zWtL>S@!osxN$TR!`D%&&%#t z{%S-B_aRt(;WZg4ty)t)X0KEts+>FnGBeZ+$?Tnp2vToX_s4h&15BVG>Nbow*pw%_ z%i!aXg2gYK7cU062Y#xw+01HXRM&oKLG?vPM;~@{cXyvJJHAFBcNVIu(H}K6mpQO0 zstl;Wf-*Ca;U7Dok&^=HCUu;Y>nzHOljn!_uqX$O+!Pa_+^O^4$;pnG$}96r8soQ} zZL*C*q(bprvH+bERPf%ZFg8J$Hg6-v)ZQjH&`rwD~oiuNcX&|fg3~<_?TiRB>%lR(b#%3qe0` zgvXNw+zCk``-DgIIuxlvv%V|$mu>7Rm$8jto_l7svz2ez6cS9#Pgv_&yO@X=?r|nd zO7c+iv30XQB+=fbf3Q4V#^;9I2tsz;ZBu{ecb#bP?lYM=d>fe#xLG%-?R!d|c_`#T zjaM4@<(y=3cltB1LzTZlZuQQG58ZvqzH#s5-`dp>a(O9Y zpp7sLo~%$nS4>P6UI{jbFkl*KT^zHr41I?B>IeMMwsTfQ-Trvh{GCydjbpn;hr*!Q zby#$cV-kxjzh-VTTelan)heq5RJ%BiXvS7zXm zNHQg85wVtP4_S%CxaXjoy#e(*83I#*E!UST>;jZ#jid@H1FZgcE557^cO`^zaIo-y zutt4MP(R4|p`@4%ra6P3zic6;%lVn#!T`ssv+ZK6URLV_+hY9OilUj_U@mQP%IA{QfW&K+*wyjslO0j!t4El!{>`{*s@uqkNc9VNnfjHy*^)#^Ir>+Qw-{GwJ7b_9%d&PS% z-}yO!^=p&Ar>=6*8KKj@H3<65nH2;^ZUCI$qX2yKb4`uj0iStTYiC55OwlaA+ruFp zrTYc_B3aLWo=lR#_hFP^HBV7cWQ*B?lbl2|VD<(X{KLWc=XVM!skgoOf@Cq})<&ph zNv6Cwmfrz+vw{{&fr-fDXryQ#WGuE2(c&m0KBOA$L}PlMQc>Et765)CUfaFHpKj%~ zWc8cw$_H|J;gY0T4xJf6G?*c_=0=ZS#B!_O3C=Us#%#DlWPDVL z4er0K*AaHrzn#0SjZK8mD?3Agk9~CyYVm6>s_KBU^6{u%n)o$``aL$zkx-P+L? zixU%7IIu{5aF9bj;K@ z^hsojYwY`gIXQIWrpJlt#HgMQ+SHGjn!n5GC@SoweOjlcr#tTI<*JbI|GA+5TYHj# z!w17x?LBm!4TNyk4c}_y?euo7Hs%`Z` z9gjF10TVhLOP=R3!5)BRKN~W7x`u)K0CSzEX8T_*H}?nw&(FF~7xiyeX#M-LkjNn3 zYG}Uq-`#~pa5k|umR?_mn>}y934ka_vf%aI2B(WHVKf@0YFYElS-0m&5n% zq21%&@8Yn+8Ij~b*0>0@X(dx$ZduyVbn|=a?tS54t@0W9eP3eJxHzRH4|*3WW!s&Gah2R$f*crl*fu5EQ@H3vl=AwQa|LI z(fzA5c@E!RcPYmu{9yj$(j2*A4|3F_=U&o%`GE=Zb-fIw^$$wKj-$p2v1RmpXmUU1 z+3ylqFeC4eSjJa=fxW;BV7uYqnH!5~_YJD=!^6XHTp;kDrn|Yh*=)8Cfl@~I^zIIVC8#nDIfRlv^2OTLFo z%mm|5fx$cD!R}MyfBTR(Q98kss;BEnDt@WN?iuDnAQfsLN~LVRix;jriJdD~}{=Az<$h^lU3nR(~)V&tnYqWNyw z6A3?$fgd?h)@`TSw89gQcp_pwoHwr5kU=3{^wby%xV5?-?4BC z-k-*dK+Xv25I(I1h2&pXr1htdSDJopY$r%?v@KX9-ksORErs94iLp?`h6>qI8=qb! z6j*BEv0T2vb+P=-pPD2lLvX~9hx@hvjkG%916p_Ni!!pZPw|{0>I#u{ zdEOz9myAT6>KnR~RU72meKD|iVIC8SzmtI2f#rdOH{k_>XN3!nG9QrHb;!a(nMad1 zFggsQ;L8gBf4Zz`kdOYNCLsg7Q)e~`8mDf|nkd7?|E4_`&BSWC-!DcBT51-ovpNz% z&mV`rO!zi01SlbN$YU@`Xm~L+veSJ5mq|V!>L}O|Bf=H0b`!FROEVWl^ei8zmSY#2 z4+s5?PxJ`3b;U2wWC0h96sz^Gdoe5K>dddo4ej+0XPJozt>JMPamEX`2?+?-={TN| z4FC^9U5O-B&A4dvJmCwae=iROyZ`Fmp4A4LO=F%eranL|={w7QbyZbFioLJ6q?8{d z$ag-POa>f?tx~jQW~To6^D9)bi;IghpDDt*?>lyih-M!5jh?m7BM)Hiu*3%7IGx9? zHSq&*Gi|Ls)*H{t<`3yA-PycXHB6|CnlY1b^Na42TGG);j!VXzwZ6@-Pc-ITUnzF#5?1F;w)?XKC9@v7LB)Jn1_u6{zW7#Dnbata)Ac%laF7_e5ty zp>RAhT81Ipp}KmBd}NqQ=bm70o?Y(ppBg){^I5TR&T{B|ma}S|{GtGFJDiQub!dX- zdZ58x=4D)09Il2$;-uXI+ad*SSKLdRxD(d6nknb%R{xOL@m*fE_j;O++&2m=u3A0u z`MDJTD9s;#X81g%$zLn#x|Y4rn*Bx;)oR?{asf&*c=;)h{bQ=gOQV5zH&!%Rt~NUz zm5BA{EdCSk9ZZe>0MU$<0Eew98(atIi(d>4GmyaOX!nUn`*t)4A0g|lSN+7~RX7&H zLjU>?$|Dm(VUB*fKG~2WV>SW0Kd+nb^Rp&(3fPHX^IDag(`z17l2(1qK`hB?oGX%L zm={WZPTUd2+5PE4UIHUC|D)Whfj8Oe zNwiAZkGgcVr=%Q&Gbiryxs3gq)xR)oU}-H$Hj#) z(Gm|q{yV#f8gI=V4{1uV2^^(7N~*1MllCfZUFhr}NkGKTwR-J7C6pqibBTS)Z*i9A zdhR!`ZEVVKu?edb5cONb1bn3HdM*P#0&c2q%-7&^6jjnsF1fB$i`29B#Ybpx10r3%3oTzmM7Z_jIg4i2U6wu>P-fl(JFgJ;K^kaQ10hczjr`nfnvNk|PX ziAIsxD(MNgS^e?D1jc`mA0eQLgq^cgZQ-W$@VI_cPI~I**u#u;Nym(oTFluWpO2^Y zTUAV91WNhyILqpCRMxl*N4w}Fu|@ydJ+ci=tRUxg(j(}h==YG>!y$D#4^-77KwT$} zt@%4&ZXS?o>~w(&{Q0vz`?XG`J>3lK%ncRs@iXq_!F7Z2$Cpy#9P&_9pyK@(gc)CB zaI(;`V~EtIUSv$3FfBGFb73hJD%s}UdE9w%wX`G()Dw=N;I`m30|(+7Su!n|4g#Kz z7iTfFSCJc@W;iD|?PyA1M<2(yXU-|f$X-HCJUq1Ho$w;i*V?6aAunLK&mOA?u=;YV z^S9UxVNQ+S@Wb_!d3!0h-M~=10sqH`+``8BDM94!5-$E}XV3_t{51RzwB)*nRn@LK z;l4>dmF^Pi8H&aj)MWjMzy=jQ-m-J8)T}cqvF&szQVqA5K|)ng6jXtHyIH%WSPsk- zTQ>Bn@f~GM1NSaE z#RuQJ84zjr(}&lueWQCR?dRooxIfT;FktCe^2ThO_!GFX%dl_+L^E32yR|;U(yOw) zxxW5o1_ndV^_RCM5lBf{8E0Xcx&BH^W)vhP|ElRwp#$m?%X9o{O;%bobQER?{sQ1> zLM$HQxqjS!MK8wPP8cC86;Z|JTw5y-thMwrM`n|-l4+lhLJfh%w6 z5SDg$B|bT=qxaXIhHUt5niHt%s8@N;E1UOZDxjTH{_9LUF~w;S>Li$`=5UuLPD9&{ zl;n$@X<(g71Iq?So;n6OxH_8!#bq(Xe2#l61BmNls{v zg9H%l(0g5WJ&3;9yckL#%tsWnGgyffmEu1*n{Yl?jCeNS@EHAb{m(W-ovIk@&joJY!;=a!UpC<4%4;Z@n? z67`+(t3s@ik?I4pb*4o@Tq*O96F6st@QjkWUMPtbNT1X#y`i-xq0GGiNQr|FD-Pv7 zDog5+z4Lv~(BQnvQ??itT%+F*gFv2woJruW#tw>K48J}WfxGsJ z;0t$FG|$r(du5$E0e%KPSyZQl|W0d~b* zDv%#vW1nZcH3tE>AQnm3vN)>`FzM#gR55l$bW4cN(^SJD0hgmVQ366^!uSO(v3Mww#@x(Kr+C?$0rNz$G8V*tTCiWW5hqM zgjxGi`>5>NXBL}br8n+uZoV@!%XNr)oXQus>aX0N7=SvBiq-GC;x8m3CiFv3F9wRL zi@V}?-g}}Vk$R3t>wNrFg9EEwN>oi3WdYxYK53TeH!~3vZ^fYzBtXY28*_4G@My&8 zx!P`j&ruaRo^LL4*I~cYnDq7Qt{fG${>KaP{_b0^3&OuQQ#p8Eno$0(z8Tk+RUfhZ zAu-P8{-!72Jau0Y^66eaLr^1^?@`;{ zWe{H_oqsQ1!vaH)kuCT|fMi41vbF}J1Q_nayO0pW+oD{kdK$vFYCQohnsZUFc6C|xA4#kCU38PfZ*%5E$;NF za)2Z*-<7_0&#pRwAX8~YZ7$AxSY8w{ifAcXOGYGxH5&EWC%MO0rjGmjxQ!S_6}i>g z@6R1K`MAWH5f1iWvAzdK1Ycd-LL?TSAp%o(3i=MlU=Q{XwpI|o#o|hJv0l1R6BIDb z_Oq2@i;r7?a|gkrH*_|gPY?lMS>Uyo0ab{B?7I1S7eHNzAi#x%N|H5jdZg7oMtUy( z4*Xyej^uZzFP1uyoFUghy@&)M`o8p?06_%1Bz0a2ErTA-9=wj0%+6L@+R%4Cbsm+V zm<8Vz3qPrw2Ed1C^s_NfFDM5?t?>D0$W9YEA7w!lhK2A}Xcag9Drm@ih5#r?qq2OV0g#0ieVfx0S6rMYeiHQ7-Sl6fLn#>s6c(2w7<)7;^BQSdrk^ zSqMSu#k$YL&MjkC9JtVnTZ{0?n@h13RQ8>-H@bH;8G=6Dr#u;KzcNO-k^^K6hX%$0 zJ|!}lAmUx#0aE4sJjg*@NUIotF)2VcLW@uM{hNN$P!zR(dN7f5V0X8GfwAiiX7#R_ zzjQ@cgQfWMP60wwfoj01Adk?WcDta!9|Sif@Jm$_R`mat^xD*wP_9ivtc1hLUgYKF z=U^Lwg#A`>qabbpkm_o_^F38CdD)N)8a;!%8zKmeg@Yp&dK(F54mP60fKQ|q&KWrW zD+{2;%8-vT)f63EdXDHW^jRd%8tnMa-AoS$Y)@~{mJ%k1SCPDmaBazYfA;IJIL-8j zQ=r2Ct4Tk4NbJR1OTuUuoHkBStXFXXGKw+dQ@;J}K0X5j!qLXL$a*rry)*kMbB@Ju z==>gFR6!EMmFB%SGQ!yL$USsF=bO+*3V8u<`#5gd!z8!GgKC zkfkRZNEiDgd*zyjsgX~wx;y0PoINEvkmK#HaR49@3q8C&*WHd_uqe7Pm+y7%4o<_i zv@WJReDIrFZf^E0a30O!w;s!<_RF_^U69kuY&7?54Q?tjbfnt}7M@qb-zXs7Rz=0@ zIhbfz$5cHEq-O7L-K+|bYVMp6>{`*fRR8;E`%^cog{#MH7Reo*CUD&iZhs2s+>F9h zthgI@+PA?a@#Q8N;76uV)|I#)q$jJkmBMbS3xIpEsV#o_hSJ4)odhN;KF7Jyf0pPc za%FQu6`#@OrC@iBx4O-bS|9-h`U-yh**Pz=-!N3C@_`rQgS)$lt{uXa6{?4vlKi?j zG2I)(Op~KAc@Ol>;NgYxf!ZQMNw;-3y72$oY11Oo@0s+q()X@~G3#|^azFPy@lP=} zJw4ZDY`>pcV=4^zaj6x8kS<5wikACa7Me2oIBVt=KWd@!(VF)}o}VOWs~B>(*HFdq zk5(fNDMhb4@}{%!!qN|#!_cp z2H6gZY@L+Xa{j;R(f^UxD}Es(CSN`gt0y#^NMLNl;S&ShKlV`6cdX?9#_KHhLAbVS zClq#|f?QYqV>u+J=^F3%V4u@DJd$1em7_~l_qK;c+M%QuH?h&aIC5GhOA#7I+^N5S z_(pF+PoCj@3Ep`;8a$YAZ=5ZT+xXZifq!Itxsa$0fdkiS9|& zdsV?IK!abO-Gi{-u*vejmlK$Sy5mVsXc|Hz7L>9~x4q7JL1tKJ7aAp6&Nz1Ujs)-1u-#+!e_ty<6 z!A}scyuA|$8QpHEp_Nxu$V@M4@4E_ZE$PVkQhM*`0Fbo6s2zGBxP`U4PXW@4COJ)1 z8K0`OMp*at>^l77s5e$p{Dh?}y-ED6np#PAwtjU`t9Y@FNV|^(tDU2I%QJC5Bl2DR z=DHM>2n=;j#_N?K&TeomWfDYZ=c8c2R4N0_o2XtB-iVHIn&31-UB_&)4%G-%N zy#%By|1iZJ)DJjGK=5gH$>qD${l0%bUEEyIJUn|FD)4T}5BP&5hkF@~h$4UUi#aI@ z`-$F%ko?vO@i!bA>4584BR{A-jn4}J$itawq75H)~}-Yb77sGWx%oH z5%}lQO6B*HjY$vltK%5=z$ASE!flRs2RD$L)@C!SdCz~%$n=>O^u-Batuv0Ahr1cN z!-&nIwbtrtYMf+bWIe45as?DVztIs8wyN*>k2s|2R~xtCz9l}9%GQps+5snoOwOtE zy?Od?LAw|QN#mPk&Pp< zYHq#DcHFs>pOQv+k5|qg!1g^eH|>U$$+%Ckv_wRFj@6~AOg$F4&COm{s4H++gxV}G zA38NN(-WU@weSuR@S@nS{2lSjC_ncMcOya&CWhV{51{X%}PCtHlq5j>Xt214&i=CxU(wbYNi@iw)!@WNir?CsVjDo5|dQ&S8Kyt zHx$XVtNREI58hON#LnQ{tRf{0QSHQ+^2=Xi6H9SW!J{=bYqtw#BSNTPiR()qtDZB# zuB|2BUc;}v_yr5Q%gff3TjjMktCT{@-yB91_+0#L6GkAYU>)XNQi+YTU}_1t0$H(V5MlcDLiMBg`dZ@4rxW4DSFP$fO+Z87cBs8P}4aS4tQtnpI z)fUS|QHhwRf-&!}udW8I{2==JY3rdBW_7RLUMJ)!dPHGA^5b=3D*@HHCv+lDVjY46b# z<5E)C`1$#(z)!w_uIOw-2V+8ns<*`a?5=Y3#n*p&QYxKqi_wT9OaTBxChs+^GVEeM zhAOM8+wM{*qh(jqirY)NnjIqDK3ElwF4rFm|D8J{A~94m#5us{%wT#aC#UoIQMjsl zs3ZBqjNq5DYW=tgK3PXc2K5iLgh~8zSdM@2d#~o_S%5GV6CoDyjL!iecv>$Y@L%Hsy*S3uD3%~ouc!I z=8t-1)`(IPd^_TS2#)28(I+TN#`&0n7mYgQyCtoP0xRW_6hIQeEoy&{b$ZK>$Zk=| zbj}lx+~{tf3nH^sOoBcW{Ms3a`fN{BkrYD9JUG?0-J~z&wx?I~=#C+3aC!^MD%Eeh z51bv&^bfQ|v59tuOwmPTZhG3C z$dkPwe{@Unf5{IJbmBR8Ve?lGDBwtIMT7b5II^WKHK4sU$HSsa z(hW($LGBc_OL)1dVE$`xO2n@Er~k;g4SM2&aHNK>`0A#i`?lH!rER-m{Gb+ET^CvV zq|YnUo~!y&i}o5r!I{cob+8Pm} z!M^oXM*q=CT+~7c&5Z4&io4Y5{IpaLb;9Tto27$3+?N&oT-4=)5>MMHg2TKCqh74q zA?{vs=E^P7KYaV&xxKS8XnGTGhSXN8Xy<8@rxx^^^q+Isdb%#ppgw3U6TAayj%M4BKAbaDaP-DV%BP1dc z$CxeSu)kixiiy&M>K$0U%DoGdcOO%6Hc7QdjPPuf85yJ!qix{vC+EQc{I3CYOJWqv z*V*O;XO1(hwtV+igpco4YpahRo`sz}NidGaxWtxYE*YSqp+P1uj~M|ZIaRrFQGze9 zP3BwYd;S5$lH4l(P#{ua?Ns!BoIvl<8-+ZkFDcXq8Gb!-FuVi4;i}c~+WK>^YKL)a z{ZDQr8+iwm%X0Thx7#73XNBTGx=snBCtB1K5BIZY*f4d&?$Q}kk>7Y6%&N)}oHzan-t zXx!DWJN$((3DR8D4IFlw9DCU$YY7_Q8hIXODWx4f}h=_{%AV_OO25#28j0Uw<#|Atyhwd^6&A-@6zyo?tts z1HXStAToU8tfwOp&Ag)GdnEGTEv9dr6{RG!Scd}9(9jV{xZx98K95iHUii1bvJ20L z%o~Gm?*ojGME=mn#>*&vOe{6g(XttI2tOJX`@ZqIjwo?X#YE&A(cK`|w-olzRa>9F z+sv~j$S5$#R^F%(0LHy~kw38yJo#7%tsEZvnnI;Odx}LG2qPimH*hl#R^v>I)euL%Y0k8Qks1lRmWW^H7d0 zr+zOB>5))Ds;Jx?Vi101p-;YV1N6=$ys8zz0_Pv67jI{t-W0i))W>!j9$bAB{JP0H zaxQmQ&2bSEW~NyD^-P`+fqE4R#^G_anLEU;aW@;^nH0R6{-N+k*`87~N}4ww7QF8% z9lzUuE~csV)g%C;aGPwTRj~B4Sy*-5iG=x7v!2Y)Bl@XduVv+BXnDkEjNCJDSZKJ` zmzUR5J=D7`e&P=Ia0Z`iMd5w^z22kcfT_u(o8FVba`OIp;+S{kfp$J3*`|~)#36sz zjecEk=e^OVknJ(S@0tZGOXJ@>2?>fs+!ZYn*SL!pry4b>bf$APm-8>WvoxQ@67trB z?(OgE=1FXaZs~E4L`QMz6%>*rP9jN96$C(Qa%4O-bbFpxeVB04K3HN_X`@;eC~zuk z;wV0?(Ok&HgLv8%I~8LI)Z*TC8l;8SDq}jf$s>%Tj#_8-#ogiGeKozme$)dq^p*RA z9Kg}>G(o!BE;-Y6yVzLw`57*1>naDBq3)_KrA@km3Wk<6yA~x)JNtv|dz}*WQg#ge zjYh#a$c8Dvw{BOLPoI)MN8_iX!QQ93Y-|1>*ZWUe4KC%Jv{!4Slb$<$yey1Ox7S95#{W%I^ z&TL0KoT&LS5%K2gskt&K#Oi~u6OZ=nv!K z2Kzj&z)BYK8kW0u4^bIF|NVjRxz+>ani>5dQaC-2BPvf;;JK0`Db+QBbj2dWIyALY zw&9?xxx!@YJp&^n*}AGvNZ#*pgszwxUuEEZCX3jh^xYa%fDz{Zg|KGtBEM}7vV!Ku|3?oeNg{96C|!xs_SwUqQCI9&b|x_clk)w%}5{|aToJ36FT*u2}+c=6M&kF zbH^<-v$Bi}$_jadAU@|fHXi+0gaOSbx7OD?&KuKd*E)33TnpOME@;g~8683n@T1-8 z@IcpsB)@NHjA}KK$tjiG9)+oaWMSW8ek^qL|8wj*5IidW(;3YZoiGZ8_-#k%2fdh( zgmcqf%VN3FFx*Fw5%#`1y#onhYo!R{5wi`$ z%(6TZ^y@d-bqi=3nLbvNIX@(KM<*uK*$rCUCj{J0QGy{WnfrfuSzuT6df3EA$A7*< zib}ftiitLy>|1ObkiUv4WfkP_f21{;94=PG$zL>+eHP+Xnm;h(xG#g_cIsS+VLABa z_KJ6^e&7K7QVDZCq=?>MzDWa~0l%j-u@C z0(d>(fCN7cAX}H%JDWYBStgSagFCfciFV~RdpkMSDKjnEn*G(UwK??WF5^eXM|^rt znwI2@ot}>x4uzUOe4{Vt(QzsZvC1^NtuVl5t&UMYY5J`?#k7NFS=(po3c|~X zWP*w^`rZW9FA9#>wo zix<@pQlDYZmT~P5OM5hFo3O_KYi<^I8W7(XnSz``KP@*gIDz$ZJE3Lbp%E=fOaZG5 zo8&5xNpP73R=qiWPDxQo_eh$^2Q^RfsN7p@C44T zI@`q$HAeo5)|{gW+8>P`u8&@ed0{;++hS1c6BAYfpM~?Cw}*)yghfbykrIB%t{XyO z|8dQJZ7VH5iEmLpYwbnm(%jPccvLw3HC}9Qr7ZSKdPdp9WQ&-E#YHs(wUAh2qqeYy zA3v74dJjjFyAN=R_Am6k$J@47SX1qmzwMV{IbY(1f!@fZvlX2pLaoefds-U2%WD_^ zISGU*ozU$Bxh2m7t<3=o9}Df$T~YF}6`Q?Np9*=rYri2_$#ZQt~vw`F{Hm`e~BJ)%YA+BUF|gJpQ$|srYl{cbH~FiM>5O zUN}f&&U)F!J>}gN4V4v!Wi5wRRyj}hZ-+-awQJV4nJ-hL1OxjPQ(NK{zujl}U)G43 zi#QjRTy5bL_r&xYKAUxQFmz3?KNl-^lc}M}fqPP0!gjHlSljBVP8VvqfU3w0lK<1= zFDV&Z=m}{n=o9NTBtDK(W!=?uC|{X8*{#f;J?0+LV%-ed&!Ve_aEg$+OHZLKk}GTMZD0wpuUPBzsd=_et?1oouXsL zKtqGWUHk`vnGNnd6MVb`Y(09%Ow~S4%9g-ocbCw7>2&)A8?oK}1VVn1g)yk0R?@Kw z*)L{Yxnd;+6p$1BaXyj+jov zlk?j5d!Dh@TOVha%z^Nt)0>ELA+9jlXF?6ugJ=#-93jF1`vjNiO03=- zrA2?%s!1v~#xV6t?0LV}lY0n=^bmuB$B3Gwu34f&reu<63`%degWdxnMHXw5%{+-n`d>bD0fsv~Ej zFW@7QfYWL8JteC;SLY2qGu(7&BR(_d$G#X^pAdv|ZP001H5hWg*5>R>eLU~UQKq9F z5i!Le4cq@^Uw5$&`fRXhgaI~`01YM0@|H5eG)ZKMpI+|lgrS^&Ezzw3Ty!_qYX&vj zQwm3A?+oM|M_Qg7Clh?bYb~Q+32GqCVPvF#?^{vu@HL~M+;!e_oJ}IY`PReb!A05X z8)gsBEW3f<-goiuXKP3!D(?mjZ*M(08WqJ(_C$8HoU&F2d-GJAf`<;fw5e5Zi zU5d^4YoDxyel3ca-QqOM_wCEJ;Vrf)Em@sFtg3-<&KLRfsL>+BuFi`eSU4e}qx$XE z4^h8M@>blP{(S8;G|K{e=bD{S$Sg3b!LD<~7jWIS za8dQQ#n|8nnayI(>P37?;@)}3kAec(8r|*b??T2jyYmqzLzYJOV7?5j z_)0=UEizr)#en)v3efy3R*?&9(cWZMQy5DcvC5NQX#DODiqX z-74M9fYK?_Eg>j1bm!30(m4zbBi%LpZlAsPvETiDJD&IblR56WhwHl5wbr@LRbX2Y zgYqOZDY|klU~K&b*X!4>{V9n^5H}>3zws4dhrcNv%V6pd=f>Fz(XajDARKA;I3tOR zaK7-@<~9&(>9@_CsKx?KC+iqH;KINn2$sHH$RuQPG{;1?vg{n>0mcB??L8A0w}=4Q zzIEX}Bmt&bmQ7ievE{^P&Xmi!1mfivFltWDeWVFjU+oEvUb|V0iaVecnY#AMN<6fh z<`3Lcs1!|ee8cmV`oasJ~yVw32Fxv!;ljGxTWS5)mIwfkq?H(SEc#C znzw|gMYW>3ZHJuC{9jNSEEdoIjI4>r0`(qW20P3~uRA5AQiHM~rsW-?TPfPnN4i!XfzLz_5$(oj>%_^3*#MX!US(s*YC^JSM}Zqcb71-m!03CKSE zt|f6H9qsPhjIR1lKkJN$B}F%5`MWos9vMo2;R#E51vu)7a%Soj;NW=>2{56%Lsc98 z??>&ng_FW*ScC<$Wg<+Qac% zd&0@)^>eIkYl|NkARzfl#?v=y0$+3(>z|NyCk#N=hYFQo;8%>5#WAy;zooE1s5Q`{ z5|7H2N8?6oqQawDX2LnG zK0C7uy3mf`c_*!D(LZvH4FUo!v71Aw>eKSH%W5#NqKj|GGr&HK&0#5Gpuxc9V>}fd zG$Q&sk^8stYew4TuxDZ-J0xkNDgq^eT?}EX?ttoA@#$75Gkz0tW;KQlatOE&v#Zj7 zIDPXVd5c8-2IO(?akFGFxUci!srOWn?mQ1CM<=SV6QLwFZvV>B__H}0yzbowg{9Qe z9%Q}VIaLQ5g$^7XJW7a7Ui9a20>J&0UjB4i{fiG|^2C*23wPF(RBx>8u*`X@67N5? z`2UJynF;3p+J_6g&<>W!&Yb5as5K94ceulWnI$v-q2k&8i;C}(CP3gteJTs+46e@b z{VncKRQ!{3VtfJF5F{ldE3>KosCBYC&#{X`6c-=6d+npqQj-sj4p&xuVTj0g)N>X0H<+TYW>ezL*D$MYnC5JtP7 zVNTeE7t!M6poJZm#o3)6^#K5_GJs1JCO1{K(rpgBLFnp5y1fd_$x03_Wq7QcCUeGe z{v%3HuL6aI$?OL_TwE%=PSm)UI1$8^vI~W9ncjWfOWhm}Bpt%4sDRjZSMH$GzrPKV ztbewFnC*YuhEmbmXLOm|fc>AQFt6GNY=4<->)UK;LWJ*+Yp#^P4S|EuwwQy>$Q;wN zU;k|l1ULloW>yS;e>Kbm8h?qn=i~o=J%6E?|0?1yfusGH5c;2>;D04G{yZivO*q%) zCwy~r^Eo#+J|ZF_sN~iv*+Yc-dqstUib_nv)3n6~>!FmvXK0(geRiuJoMZi+aDFd4 z>VHF%;o;$gPo8W!>iRGPYKXRLr$q+9lQec9+DM2g=+}1D-_>h?zU!1iQ_B(w?(0+N z>FZUdGKF2?N^QT3K6De0%J6t5adL%itQUiL);F>}Ig+>6l`o zoU_`ed;i*#ZO+h+E*~|YT5S@9FG;aAcEQnXwQ+aU|H<+E=dK0KV${~x=T}z~ajaq$ z6@Dy-Kx7JDbuG2m*4CQs)w3@`yt9cCp7J00*IG3UH@^Y&jluP4G6bq0?sP;Z4RZo! zG1Y&D0PXF^?4SN^5QBsn!>VCu7!7oOl$JNz8-JmVg^5X#*y}N$MF*C3HulV>)B245 zWU5N9*8rG|J}@#O_!{Tp!UxGvnw0SO{oq9`uHq?wuMxc2el-3;#Rd3+@X*ned;S=e zdNK{$CYoQXK^-02UEPNY?EaMa-~Rsp_2Dos%EP(sb&3O^ zV^j7i;csJrP|;bau$>GHEWBH7|MwB>KRg*M{pXbCat5obixV9 z)_oMz7QOgr*7my({Xcx?@43c*dC=IzUxA?y+kjl9gm0z-^6yXPQ}_{34rol+4FAM? z{`1!{oKXOXxi9_yhnVpKU;Y{G`;Xf#jYOsyO-r|q^?*An{PK}E5XMDqMb!`Z*IxIP z6%e#>ujAiTO8-s^04}8rZ&lpCrhVMfkwQD%nl6{^0Tu4}5$nhvc%3(T^^IFp5HOAS zVt>))2xuOCIG!i_y|gq_3ujJ`=;(29Ru*-cVFMm~7A%YaLLgz^zq2_}M_K=K`m{3! z0}y&9dpA4QKN=LIbtNWzlU|PFk1{6pAWrMEL70@9iXLb=heA{-5)4E~?^e;JIwdZb zERQp)egCQTx`qB(=C@+o2{kN`Ei!MA=eHx_Akk>;>_oK3j~^dZ#RuT1OI{aFAb<{1 z->G)@rc0}AMkqHnHyOmmsq^yk+`iybsiM>{031q8SJ%s_sj1bSi2@PN-)XRt02t&^ z!CW|9Ah{GUq?3?>;zee+vnem3_kP`M>)Yo*&xkRC#?2H%ajkLYZ%5Q=XgIolu}GE+ zcJK6`VGc(ijSW(mud5-y4mzg&N<&6JZR7t)Nm2i6Mhq#oyG)R7FdxY(4Y~BRZP_CY z+o}?NmQImeKGB1BnkXxA@1un>rvVZ&jR8=?wiu2)`+r#^hkv(7!OE-abPpmxnr|*W z2M1Q}%~bIb3?D?!$H#|(jZJl5a3t(}n<{w8iwx|&fB@Pl7!1ajdbl1tHhnlZtz6u{ zitf0=zPsv|x^R-;-kDIb(~5)*yrT;zxORxl%T5@KLt%D+o%{Yw0V0lJtF5fi7V zYgnh*`@?bLn7ZZjAz^Ci4B*RM0m7GoWDsD@FZQe5a=l zm>mpgczDs#Y4R| zciQM?_kOqg4ae7GAX>a|RNl5Tq62`>#(cfX;Y8$2DiIfhW9j{lfT>%SJmKNl^N-WY z{B1toDZufwR6caRjMO%p+vMfq!m6)ODEPgL0b&(J_cc(qHCB8gjWWqVKuFP>mV@_f z>nSoX@#>3R>rSqfH^7Qh_Ts*l;;eJ-G*8*#T9kR??q{*5@d?;AFC5}pmtD7E>mkz- zm}T7}J;#S>q142_&(%pmXF?`A68mFcS02rP0TG@#WKy4U=D`G^_fAxS!KR4cJ?rZwj~`Vd;-XTu^k0 z5hbWc>+w=?bcmyHHVzI*I=?LeAiMpM*Y(#G5NKW(GR~Qo<-kNd%$yJ@n!tN9jI_O+ zAYK7wl34>fZr`dj?sTXK#Fcz~eXV@R@mfaM-itz@k)y%S&eUrZhSgANm_Fy44y5JK z$Q-Dm&_3;S`&A&KpVo`{Y=wK}C9;V`pKg%XSNzg3y<>*o^b#bNY)WD?A4e?8^eeUW zp2icKW%*IFW1sN{&-c~lmX37;Sm-JZ*aOMOv=QNeDt)C-B^`b7s}n1a0i9Q&9@`3(eTH}-k&?CLpq914`uNsfT1~9;>SJ2eJ^&q zx@*3(V>Duc(9v(ko$R`U+7z=uG>ixuh?wJ&xIE^P3JT(>8Qe`o{>yz^<-GmQ?g0_9 zbBBspOq8nPmDt3KN~>eW2cgf3xo&7giC=&c?26fWGfqHS-fym$FN>=N9Ur{U=Nd~! zS|3V&ybV?es&W++UE0D!?)xm3l-5s-MsAn$b>MlKT9Aw5IjzNgk+NAmpTzPypA_IL zn*Fg~mA7N8b%3#EO}h{L92rs>2u=#;7$2XC+84X*o>|=XjO1f%UiKcr;$r7<_sgBP z@;?X_9$(rcL40_DzwAZzg=pPYi}@0l2*nE;Ap9rkL;Pl^!275R9ar~*)|qlYlz!Jr zsQVDDhzPNF0YzLiTpf6IM7JC?WAnBieT2frQ#n>S?mTeL^1&_hyEkI*`XewvPVLu4 zO(CBgaHUZI*1^ufgTSj@WtCvUNtolOa_o%CUz_8+Uj#?}j8#g#gGN3Xyz;3GR7tq{ zg2G%$()#@e_eO_$G;-6Lry^b#wq?WF?H4-A6tXLfL$3YAW@WLgkWDk@P7(Y* zlk7!@3{{me2UmLu%wY+X8y^@vVP6*OiDToh50jy7BYk5F+`B0gS8zB-k*}#)c&$GxK=KZHz%m3J6qm?!TT{f}kxC zB6M_h*AcgeiV6ykGcq!`mNHgL7sz4Y<*=0lmbu&JXIv0dkGt&G0u=sobNSLtUm?)r zAEh;@#cH(tI_jj|TG^D0zN%4+4Hb;Nm&5_3rp*N7Gj zKen#It8(@r=X`RIh4h2cl1*Ba1EXY65BsB<6>$-;=v*Q~el+sn-5&QK!Px1ifMIz0 z$1q?z^ZZr=Fw%}SIgeG>%r7n5p-R6FEe+U0eZV`m8_ijp0+XFLM-$~an zxMg&D!@#1hCZ1c=BrdzpetU`uIjzKe{!C!$qlS9wvaUL@{&r1?PB%K7DOjJ_S-5`0 z-)`1e3~pJCM0A7}JTUMbGkU%Om^T_&GG^Cz7IM}21WiMh%B&39V3(Y3`6Oz1CgU;|uciB8zekS;JLF9b$FH07hkMNW zmG%kW(8!>ZjOnL(?S&W#j8`a&Khf4bFqax!Vw{(rf=H_{pGk&hY5L5ME zI2F}zt-ilMTxkm%&<$4HAe|D&JM_0ac>jFrz8vXvZGQjpG z?rb>D3ah7x=*vi%`++CgwV1~)t7eiEVZc{(?gw=| zl(ki4rOc!>hgVU=Mn}iX>exK_@+EP@x|Mw0;0_DP=p{-ZBZ1QP#@9BwfMuGULwM*i z-#dcnC=5k+6)p>+FNCzD`>avo_FoAw3cc}3ipsXFQFNks1xwyJ9LkvH2GoA6IOjij z@-wPRYoNM}4rXJsn_&E(3o>=~PXxAu~`}A+&&Zk=r za&oSO)OoKk;?-8YvTVBttH0^Ph$-h~-1goRd&Z)Yg~zR}_xpT7j!iC1QCr)w`oa^CMfAA!jdW~F zq2jMHK?3?Qpiv^gA(qtcIDD~vjFs4;}yOs`u-iGVa1hGr@|m~xXdRaS{Wls(vs<8 zW)SJv;6cjcsT_&0unZ0XuNS?{6p_aRqny$QzowOx#_$f*%;BvHol(aJb$!*q^tvNO z|LG}VOK6_f_i{VedfglZTtbEv`FrDR+Fms|S_tJN!S>s&X#wfey_*S#t!9nw3yt&= zX%QxF3gu|*gq)1uZWUeL(x9H#&s?*H9I9n-JQ`984wg!H9JwM^}-i zgcDDuxczmeZP?GsAF7;P}>ym8&jS zBQ@#uDSk9wj(WxLm^vstG8+@`prJ8dE|^alz#T8ooQ>u2^9=*;95HUr3E=MLv|B0P z$*y*x(}hHVybhKk=jP@xIbXsKL-e?6evwGr$X+c%7e8I3DD1fBGpFK^Q&Ycmw-Vn;GBiBw z=?o}K%|A^Z3**6*#(pYx?b#6~W<;8}EYQ+YqGboQ5pccKC?s8%z^JUoOvTV+Z|C5E zR|d?!d^;;Z$ut`p9uBba+IJr$&Q01^hQ_a*UDRe1_Od>jk)l$$sku~EOj|FjKd1U= zrk}DLOO{(x^Q+3m0n@dbpB#OX-t3!RxahtFs6s5f(md!i0lm zI3?}xZfR4o{N8rOx;~4b zC@72s9xsUTbez;cFJ3 z7#<&g#!>QN;@3hjts(EtK59g4oJ@Y>EHmp*#imj|IrJ;a|jh& zwB;#KSD&XE<~5nw=u*WuprZa#)0~!Zqpzany|Xh0w*$>{6(d#y>xF5D#OnNnlrJ&0 zQ_L>jN_>vYrsd)endP%DxFTnmjExus1cBJoods^QU)FwHT$G%@Ki8AjH2j4X74P*9 zxO8=93yLZxRtu|AlbjUU<2C78oN$&(#+S*<~EVSTRqzb{l+L*F`^v zm-2TMqD~)Q0$t3x+Y#qrs{500sXJtBvS#6_VXIr!`V|qR2Zmr5v>cx;3jdqQT%no3 zi<;ct+4hS%mls*lBnY*L-shaE!D&(<9p~ECBCWTiz+Dh{_UvO$j`=VrTVHGgG!C-1 z7Fq2=gQ=p_xjZLys(B^Gpiogu5R5tr6u?-h(7kZQh<`X!JXy>qUUY22m6snKJ3l$W zoO~-5d(7skDQoj=jyRlrT9+$WyIA*bFtD!~Mrip! zYnbcelnOB&lTVeh=yj{JeWgQh7E1m+v_lq;>O|R5_Dz`I<>_t9-h`$XL0fjLb4&;m zj|bt_#Puwexu7}C9fe&DA`V{B<9yE(a*?Gtr9@V}#Y@L@=TIV!tEl&d;m){rH$Z8~ zkdFbi0w)j!5pz0I$8|i$=Y;FbBrLwT_9a<5&a#Jg zSdOD@er(~RNs6^C04_kqYv2MH3nroq1DOvm)7sghAf~1o7Y}@AMW)ZbS+b5;-43f*&2VZx(uMn8K;)^VJoHKs&sKEh=cBkylWNWSmS|k%h}B!%js`Tc$*c_iy8B4I!PaXO30^EJA&n|(uTEEKdxTY$2z1L{)=FOVUIm@C`^|>SIPZhT) zX+=eJA%}&Kl$0k0%f1!gy&bPlHg%oTUjJb-tVCe;OFRC-%yaGhU?htw(xROSItUbt zu+0A9o>wyfL7oqh-Qd?X`og2?p)cQ&EQmMEX%LDIvzcSR3ab&2Ae ztk^msWX_z#k?Bp58b2{g_)k6(WVzm6hNn+a8J-)+cvK zQyY5Q^Jx;6%a{VD-e4&fqDYsCuo{HiE#6=aUO0KgyI;3^&2)*|n0hb5MEBIco(yPV zkY*mqQ_(7XIN)eNI;+?H`psW#0j@qcelc>goMJBW!1C*QmOF6er&X_B=B}5~oRSoy zq9~!DMl{=^{&m}G>tSwE9r?B0fe?WXT>x@+teRHw_dCr07l3&my8eslM6%|4)& zk#C0`UG|h#4mqu60l*_olNsFfa|e5}XL{NwE`uMu+hX!&lsiIcK-Mo@wPHIgfgZXY zcR(qS(vECs_~^jr!K+wbw8k*G)5>M-piQ^5KKdMB$c|`{wX`6|8y*8*S48`(p z!FCdf#KZ)gaYQ#v0VQvl)!LQGM*4>qp<$v3wd8}PdW!3&d14ze7K@#G#=RWJ;SyZM$PEd{kY?4O;T6%*6Y)_z|L{`yrT3zW^2J3K#X z(~bHuNNQ{_!_|kv?-tv6F0=AOfyli=X#Q^%Zb~~Vk~~LrSFFj(bDr&h1RCBPX=7{7 zH6q5($*D1+Pn*a;7Z>zj>e4fE5YIZcQ2EH;Et3(v-0pvUBf7FL*wHmEE&R9{9=mIw zL7P)aP}O9i^I1Ch)?UQd&D69%wfbDSJM@TePsKiLVH1sA_L0B^?pm z?g?#vRQm9nRg!V2TQ4$5YP}U0lB(Wng8_0&UH)iAh)@WPp|{TnGN9sghV@X5>*8YT zuEeastf*h;#Jy}mReoZ{AXJMwl6-3hFY5wo(Jlm(Z7-w&4^su*pheFV2vN)vKNFO% z;T>!v0>YZ{iPo-~S9a+YM<3*iIP=dwSc_{ku3X(s*YL`QHdVZWYVZ=7!%b%m+i`35M>XPJ*{QkW`PQ_mz7Jg0 z*$`a&qk!tu+W2n98SeR|gfqH;6aCXu<(tl6RAl(o?Kj?lLFsn8*^2is5TjNd(tCz8 z(|%|d2g}i3ZE#~c&9?*-XA4yoX!q-V@5fbqs1b*oeP;}LG-0zjbk-OR6p)+Z|JubzdVIP4pz zno3iV;)y&$DbkQGz9D;xA56TqE+_CzyeLc0Co=Z^0ipBdR*kTwZ~YX2Qb+5J4%AT) z^s8fT)fFYw=w59s9|u^mR)n1N%WiE&sFvUG$Tdl#h~M?UW(^;$&8LK%@fJrIJ~fs4() z@?5|DSMnHPQ$2d34{_nYd#yWq#v}YwVsHkcbprsZRYAQJ<0*Va4q!Q4bz9Lj2bISs z?Z_AoKf8dn52H((qSdBfiocf3{X(>vt_(t5uv-ZeeZYHhGvNex7kyB$r4IlcAOQMB zC-C3r-xJxTmc01uzUmMna^!8+Fn+m`F|ja=u<0ov z+WTGcj~Y^1lVlMu^3lh2Gz~QjIxREyL>vh(9UXY&x@*ZkRim9}hd>FqjA}^ZKFehh zzz#pRey^!zqas}UXsqo?knK=+HNe`w71%XQ@fmQJcZ2MyIaN`ZpK+qxWI|Jcq@91v z31=jiSFt7)bp7Q1P?VnYC&v(2`RoigJjOGV>MeCS3ha2a?XYz}jB|;WrL?%2oYzDz zXex=hV6NsH+40N@6LIrx$&pUzSW$tKmpZTK!HbxM81ft<|Y?x$|I#&cywZ)5^aZujbnd^GL_1@{<**lKv@$oIagNCk#;E2pB8lI zqQbnozrRmv3s2*_-|}4=yl@-gpT7GX(XsE@BBIr=mYlr!DWVe?D&g3?3)HoEIYM-v zvanc;>qzDUYTBwi5$1Mk5skl{!Z&8KYA)a82{i89<#nBJi1zLyJF| zo#c=CNr}EiTAsfSep=AKknH-0#atmk=uFp{&35rW{!TR$JtyZVeJJMs;jCGu7scHC z#J07P)KUBhHx&*(ayF)8nN1qhdt=1b-H#W?UQ8bjLAF!O_4=?4z)PpTGkZfIhG&oI z3gRR@+I}%VzYy{B*ZMmhp{u#lRdp?dElKu;Mw)s|Eifi|Xnm6|PRXpu+mqpo!vR3YlYg*WHXB*PGT0hJv zURo4sjUO0=aq$W`dkF5{voA|JzOW3wYmZO_FHuoNa(q1nlYUa(+kjua!ST&?hr+ZN zYpFU8qAkD7dLc&7=iG=p8vx3r}z;Wkvyh-+P<>+BZaw%SK?vvKIXw%9tiNN zrRx0j2x#Q{vec?#(!p6i&jRZCc_o^u){%^Bp}=TdG1@-VLpaWJP|AU(WA5x?BAPOA zSzUSx-aP{=P0@Wl}+bE(jX;v&gGA z74Vzp*f}>GdGgVY7s@+#@V1?FlR-p;U`-hReH?Z_QvO6}@CEiecHM-g(;5ywZZe@XU^ZTzpLbu%Jw-@cI~It9zVeM9@@bT%`5+Z~Va$TmwjTI+CkT@B{cjs@Z;BZojS znl44N-|FhdcB6ZCGnnoT%{aXLa3e0_YmY%kpk;d^JuxH9Na{n(OB-D$nnP zMC2@7gj)H&>^bJ=PA<%}OJp1Un8g-=kQd^xvjWnKDeqVJ4bTdXbkk|1v^j)dsjKbW zh~)fOK^+-Zl7G1(PQp9Dzckdh?P45w(!d+%QabV_;IRGtaks~eVD4x`o!43u8B`!l zgTG<9$sszzG#gSAiB=~c+SDj7*R!5@G5v6V?9nvw>qYbXR$1`YbvE_bw1bFo2*u^{ zbsT3{z>bNkY@tp4l|CB5E;3R5EDX}8_{YsQT>1uu%k+EzE~WXl=(D9)wa zWv|Q#mHy^V+=0xyae$L$rbn6O} ze$!i3stfM>gfAI^A9(ahm*jX{7C*1F6nnAWq5DsC+qCK)h~QtHq;}YsJpfU^$XF_2 z%2TP^`7wpi4sZuT^sEsCP$(KYI$>8=-em@LSol(R&sJ_%F5}oIJvtgzeC?*#ufC<4 zKrG`WY&PyE;sZnO?(X<*1%-rUW;wx3igD79`hbpGbJf<^h&?a@YbX^x<;YKEy>azP zFzB`c)a})Nnq9ipcG!MoBDNDG9=ZLh8X!H4V4Z_UL!r#9=*yol)Z(KouB^v-iuRomz%XGf}k0?Z?hd@D19bT*lnWlc=lUL8F56^|c zovlmmgd(Ej(Cr}O(IJiNSSr?%)tq|CB)Zap24*Hbgx$|KTV#G(`B{^D1NXT_^9Gg7+q=`_?2(P`XQy4NRjW>HYF3AJ~KN+7k z3Wklp?b%q08hG2p5$hYYdLxydn`*ws}oJ`ByiH>^fryv{w^@;_?NA#Bm=PYdJxFAsVb?BLGtT_sSK6lTjNstL`5K95|}NZ zYjzJfKbkm)W!)$X|2XK}C+WTyQ+UPePJ=py)=x$UjvI=K9{XJ$@S7~L8#EimK%Nt_ zhA}6k(moOv6og$3CXVkGnEEp-`FS4*+5w^J%ZRF8Ac3c-Xh4@4$V4W6k53a0F~twA z_2%p1xYPcvI^r}XOt$1G>C4y#yg0`<6TK2%_B6&7KkRy< z!TYqvc*J}=El?l+H%3_ziozxtRnf`^;N06pkyjDrqu<+r9kDjO>HPb$><_VVpntUG+K8QloUO81VUFnmUmlE~x3<>dV9-1G7mocV=L zH*9QI9bKN|6JxG zw@p|eRzvXZZ=msk!R3XgOKlqc54M{~)|^gbuVuk5oeHeSD4Uy`jj%APaVS*E+ASyW z<~>jrUalYsbQS5@oW&`U9tIe)6Kva~y`i!Ejs|r2tZ94}^{MqIbIW(oXtL5u%YI3@ z{-F*?~|| z_s7$*G>jaP3NO_j3xRXuD+$r@XvJa`ngZjZqGUoM0?!S42p*!wf!-&YC$_2Z-jo?U z=An0Fa9zOLP(oXg^d!{w7&$QmL%low@0*F(y-w^bmA&vdg_%&oFAkqyWbU0VQ1MCV zhf?(>?|I7;Q*O%Rs_GBA=+)5^>$4L_|Hy(sov3{N5O%N|xGhiPtlf44G!VO3JxAKW zx7nzm74?*i>)9ylI?`P@TB}MY-L_RjBkpvrSlDPErO4x(iIs`qy|BlHRthkh1}^#6 zZ^S?FxPQ&7z#hq#h=_>oz6+I)APB!sIr$J0f@`tz3+I!{z5cZtMbux!HaTW*Z?&t4 zuY*$R^J+KjXHJ73m|Svh1O*0uCRSFS;;A@f846c4>zV% zQ;PwH%U^z+t2=gy2s#BjJN^$Gql_Fpq(eeWpO{@r59{YQn}sqHA}plYM3+p!Y~YyY{g6hSL@T*BQqgWc~1u+|Ee5yqSlsE1zDmVuS^ zwklR_ON;VB??HcpEc#}P01jN(BwEl8oN!w;YS;YAYa=WOaKlBMh_`pE-EPfN5C5o7@}HMjwSQee?_X*m~S>h((hPo)MB*yhz`N&_lOnw!IHlC-B* zEmUc4Zhmj=m8vnxtvZ)B9&BxjWc!Q^hsR|HBhugJU<5*+LZGGJX6o^>$XFkdmX#>U z(0HhBFCIgncC+OK6Y#ZH(RLr+lPN-n@>NOCB)!LgIf3d~JiR%eSQS#WTT2;Ne*E%w zIZ25vt@8ijMlQ4LsL3k9e5tQ(`TRP?vOZ;W(rYE z-8z$JmzMa`&44JL3$5hZBQ6NHkUt z@1zdrYx>J|NJ&Uyn%@Hak8wv}o92bl)u=mme*E)vl&T*gRx95}d!x5|DDE%coMEg~ zog1e~fg)YfU@&AC!7AAAc{guZ=spH+bcpDyQavxxSJ_kNHHXQ%CJC8t#HN`$whur; z>nC*pYAUIz<)|GyZUz8!E^ey*=@n2|)2B@28~4m7HK?V^VXLJN6=FI`p^_blpHn8C zyA;5aksG}RvF939?_QPWRm)}GPEWDG2)MOko6~5@%mS)bC)REXAsGkoqANaVNJ#w^`~`!%3HP>u%2`1=^-M(?|8KZ8ks{|OwRKBIg#mgi_@x` z=@sW8gjV;)?*LSswFsudXFBiwYUtQT(}93)_ie7NS)sA`Dg23zsP~;&dHhZd|3!1+ z++6<++xDqYrl>D2Yt31YUYXwK;E%)1%>9w4VDUmv)fVVN0pXK-+(gk#l&BivFPx@xPaQjKXzN3tD2)Y_OK8(Ll?Jw0dH4Wbfw=?q6 z3ImCuaF^wx5n8D}t49XAS@u6FI@2Ys>h-zJlZXPT#`7C<(P zH;1zxvCX?<|2id$vA7;>Xb+}C^mqIe%&mHVAmTrWM+OiGdw2FoPR8l-IrDFl=g!gd>YYP07LQ$U(oeSXY<$(~FyQZ)NUVq;+ z>!rd%tJa$v9xg7=9KZvJnT@IfP%tW-%FtOawBrP_{3Y~SN!`Q5FJi^r6JrZfOE?o^`;x!iasV4MCqS3#&N>5aUwf4?Pr1W-AQMLF|OL zYjA|axaLj(L-6EhuA25Z-2KL(-?|v; zPZFsN%7>kaUHJOk9UG-SoSj5pm3G{5QEoK9s}^uF!E1kTiieH1th~^FV%f%flQ!!# z%0pfyY*~0(>mTRN#3f2Wlik*+;fovxbKW4Skba|p%xJm6uOzAdxCiE?WFoF{3|^s| z{=tqSlv1Z;%=;j(C_lhosdrYJ)jKcIk>oFZ9Cm36sG~u0*ZNVhhno{1>3{npsT8;m z)c>?s9x}7c!;5!8acp^(;U6R~*GDhJ(3_?~ z@88C~u=WBS4h!knc27jPtW2x~{j4r<9mK~l}iU~S*(|$<&VOLUC(i>-d ztEsifd}&?)ZfwZsE6a!Gy+m)no*rA9;Fb8;54y0_&k?CN9ufl4`6CiK`p_Tlx+9Xw z+$6Fqo=C0Sah==D=hz5n`qaekM*iFo1)>S7EM^~~{HI?7=^FA2vi*5Sv$FKuCc{R= ze03h~c@^!L95~Z->3ar3`oF?Ic|m>K_>!v9w?~%IaS6dqtvZ3?j(ROiMp+l`tzUKG zQ^gLeCl9Vr{QPc@f!Q8$6>6^uE*Ec|sEh;c4hgd)P5GL$z9fnKkoFQt9L{1df0mX8 z*GVF=JPp&{S|f!(#TpzG0uOrd!~&jAo$@|hhE4>?Zie6V)3%Q}$5I*ZK6l%*r|>+K z5^A<#gdOhmVcJY>=4XIVN8}-G2`Q;}ths_Tzm| z1g9Gq8dklZWZ8+ur{UQeNMK^DjN=mmaDHk3+f*1(NpSjk4WSW9<g^Dd4 zpH3+5MS7=K_4%I0UG|}WKig$Z?mgF^=Dy}<b}=RQQY z<3{`J;-YyP`G&C@U?{?Hse~Gnq01^`;-@%>>)Zz)l?y14?X1^$bP_FPDdgH^2@uUd zJW!Cz=J3i;8~Sk>o%hAPf*cbk(<9DkH{9@*kU6F9^y9f7r>^$`F{H|5bhLiLgF z=+S)EE|Ss+Y$JlGG8TJ-%icOFhMVn2JylYDrQmfK1f%y~lPMQ@++aBeb4M&!n`D8u zzfmoD9(bc(LgCb;ooLs+ya5!Z*L$-g^0_KbE6tewaWoVXGtK8z$3N}F6jNXD^P`00 ztBf7b5(5}tm8xg=QqQLVBT$~1&Ejzjj_J}olkCV(;-ru?B|vjFvtiG#XMu2Ghf=0Q z2*f_Liv)nkE;#BBd#~1=K^KVd&8PL#mUzny6-BuZORPr_x*WP1#0Tt#v$UumA=dgc4>=kR+x!qh@@xG`sJ(V_P- z+8#&mG#dkozR#eZ-j`@(AM?#C1Qt6l>a3$5t1Fq&6Jo?jDuPJr{unfdwK&^xP2-Qi zHqFwZSc$88uWf^8d*RV4+*$&Ga=F}j1k%;&&?Rc(KTb$%Qc^&Dm6$^wEQ;hMm`r)v zY%;lNyT=^y?oGISuDH5mPr~=?a}KNQ%VO)uG$So$T0lv`lLIkC1w<%)GAUdRVkuZIy)wXz62zy)If9&$Z^4H6W zhwXl;{`7}^QtBqd7ev7#mz@papN?s ztlX0!-!$-A`Po6{tdN(=&RHg7$NiGW%``kO-hb!*s{3>-7Cc%w&zsyY5p`EPl~a$Ri!csp%V z3?6r+gq0a}B60-cY#mMR_mvf??>%i8XTTZp7>dcQ#sq2>w9szFPl|bVa6q&+C{9J#-UPJ@@Nh ztP-U<01}!i)LhiT;@r7EPS#4=Z6T8%L6pr{|jB4gwAC z^i2HdRW6px-~Akx-#M0qs}@18ev%yz#)HVmBEAPSvr9@MFGA;H*W{y%Tj1KKrrd$8 z*ZO-S)!+p)UMF|qqBbMkCqU_=*~`BuB5j~JT(?ldof^p3M*FqayMqsJ%BbBhcgX%; z_Jb_EnzG3U=DzN{nFpCiKWwvhpH>F%D|lp2etWBH0$QfzGBgrs4ru0I5A8S%+#Xrm zc-B;E+7a;q1Y<0r;k^rhO*d$83R~M`p19RGZ`X4d^u~a<1H9T~jl8rJ8(KzV$PgvU zOSe78Ue)e$A=T% zhmWAUevp8U#QoW1<9_{OjE_17XuAAl5$cJ>ZWMo=4EBHYa1R|tuN{-T0> zl#uZlHLkJLMu8r-K?w9x9pgau?I@X|rjHQldz4c%&p*NI2G0}-v}UvJyWNL=29Fz> z#*H(>&$04rXPGG*{u9M7!$>53PqGGl6f14y`PM8f<=pA%!uM&R&)CIO{I)xq~GDF zdohK2-;WxeabmI`PZW%HD8^Gmh~ppT$amMa92(4DCTOK;Ynp`3Hgs4RSqAPf(UFlY zdK82^IV2~r3YE)hO>RQNn>C&4t)mO{9UXIGoF{_zN`6iR45xdw!zJ&2N18sG{@ic+ zaLcU_bZ& zn0l+IHrqB@H@HJv+^Ilum*P;MNO3J1DDJelCM{MRio3hJ6DV$_xD(u61B9^h{cG<% zHb;!XQAqMW_cP};=erj7X>o5V1Q59hJiLfbzdVP4HeE$!vzSq?ANs+jq@h)W5 zQDt%vjTz{|bA?(8%xdmnH}vh}+>B4GozBQDXWDj<_8MEF4ARWXnhw>`b< zJ=fm@D2#-XG`OL`%{#Y;u+xezoBtEyDbyUebRkE7_2B5BcI~N~ceF_uiO>I*M;MI< zr!+YOHQ)bC)V?MW z)c-lj=QuV?guDXgZ5c3Vwh-+;dKpWz8k5u(|NVsjHr2}EE7O##E(519kr{8<$6t{x zf*Qzr>4?L(5I(^jB&{m0Fo<~joj~x_*GSg1_)Iegt>m+DYCUQu{;Hk*JHxMEUu{~F z@IRfLQc$?8M%wTUE+Tf9ztk02j6Jm0anGL7s`Vc_cp32g^=&hpR5xVJjzUuye$i7E z`ITC5B~+c#U0UmxQF^dkA0_Ce#`jFFs>32=pD1Wt+t@e|+1o#?(BjX`Zp8c+b%f75 z8Pn6PA@_0vN3X-xr9o^hWzBYuu`z+*jTy_H>!^FUw4ztrPkAgq)e{mdAr! zoslQw6PnIR;#uiJNycqn$`_|=zO?nkod;<=Feu}Z3nT|a zw%Y%L%Gz&fl0sF@K`W&I-Qb!u#98n>5+CPjJ30|^{%E$=F}ZVqJPc+e+@OvMN;Hpi zS@x+~I3Uo|@#ub2>mI|t!71z>$v$=5tr^Pe#Z(tNO^Xp1B89h$&FzcF7B=`SLfC7& z0lOhnsCh8iwQ)y^3_z$c))#e~lc`15j$1e^UN8U-a zV9RrJ;Eom4EvA|#moIc{M=I?_MT8mRhFne_txkBL^0KnXiO*3S^95WM`+M2TwtX>Qq`o*1p zBJye=j@a7L%Sv>Yd`zaJqh;3fAnn5^hu%NEnfD^wa;*`w1i!+PUN2g1M;t2`qooo) zmi`$@8z5{!Qa+=xe_{eQ1CUo!Dzh%hg*aOg#9zUp_jYFfhD6auHr%pZFU@i{8OTB? zUUFQ2b`We@{Kt4v8m)b3lEhpq3Hmi*+C%Dp|0hZXU242Iz)G{!&T4_FSA6;X zbAJJSreR>@#gxawBlUK_iZBqDFD~}oo8KERyi4f965RIg7uKc%5nU+XPUxgPiwR~E zRW3IZZB+B><9_P|!R6TE5=3rl+Mf~5(B9_f4SREL@tf=VmZ#|U!HTVqro`v&9mqm} z)Hm10W?=hD36}W_B*-A^hWgM({cnC2VCdV4UvDojo4{8^drS8gBC#^RT_cLo+D1b~ zW>!I+1f^^XK##+Y82%``A|^X9AtU?oq&F{$BaItZ*r_c+C6&MQQ*L>9wcjK0v?aA%ioWcx$#2MSQJUyH|4)40|fLI8s zGBDn1mpb7K5%}n1_71+p;S^6$hxqERi_agWNU*?}4W`!CqYho(#U&r?IP??yzC-ur z9FHlKHFabwyE}#*ar_+^*%WkiT^_%VdJ5=}TBwlXua_5n{X06>Omu&nB_E9s2-!zN ze3m&#gsa1_4)5h3ScsX%?bHT7Ejw>Pkj<{|(HQDB8I+Hcr zT_|f0FXcp#ut0_1t10&ntv(k9zd)Xd>_Jup(G&iPUvT#M4z=%T=!$o=UhGT7_jZ(^ zcET*VwI6ot)ve314=0HBs;egsqcMb1^tXO*7Lr{JRmV=lxZ-MYw#pJx!f-q`7PiWS zk#eI{m$tW!9_nmHBf#do25`7*e9&|chZK1BZT==kKCHWIwP8*N>itOI6L6r#o7%$J z@+e`@v`J7&{}({ECYl}arF_k|^FGP&VD(ONa-e=?X14p@soY(S_v!R&(^cLdBPF$n z!n!>6x<<~oH5>y@DZuEddr_ECiN z;saaOq=s52W4Ho`XAErVI23uRe2ThEYCD9cE@}2W`hIeh(MTm1RHzCbM&*%cCM%hd*C>12FG` zCIeLhFsBqJF=m=b*15U|nIlNFM!b<*G;1#u8*a{?8b@jM%O3Q=>VV6}J^Q}YFOEUa zBIkPK$K~h%YCL2V#U`#K23g2)d2TwcPOGSgj5Gqvh0I_HZbErl=>` z0ZN)Lveu%nb0?+{GqMuJzZ-}H(=p-b2AbQ?bX>gKzt}_tO?$br2mOFJHF6D7JNx~L z`+Hov@qVjS@`jHJ<_q<|n?yW4>Lgwxft<+-{d(;Hm-SfB-Lcd$Bl2Qg-@8>$9Ob^w`D)x_EAD!$nba#5-JIH$}>Au_{<6VedEMc`vE%95SDOK~8 z#Jw`#1zKBST7?WK3&vraqK;4!6ThVkY~>!v%@X&GPmY1iHEeeI``R8BT!_81A+&fw z>5d;F(I8;lv#}xWIj-SNM8lu*<0YsZ?nY}l_FGri;12^nb&udoY+bgK!GY6XAqypH zyOCV_-LJ3#)|5h0|E|o;PIiw@w#`3jSP8F;Q{sj*=lLnl#yHxZLUQ5hs5CvkKX0mP zY7`>*PESt_`srmRY{Q@XVN7`Snk+llJR_e)2Lbh(BW43becHMtwpuFw?f0=*^qOS4 zZCWCrml@&lWmcwY56E)k&RY ztQJ50r8OQvn-rX6nTCdV0IY`qdIaG3Rw(uEOl*dFbsnc51tqqImUxu4V_3R@$koEL z>DMduYGq}&t>j6Qf8>bD;b~hoxaZBnju)DzYAE4LAm)so&YhV~*A%*SXVQ3G?O)lW z(LL_7!Y{R7Tu9cjsJ(;cFf^@fbni}lPT6S=?|G|>|E8xgXkMOCyT{d^O?S-RTt{Um zQ71uXjKTCwVq4NwLb&Hf3kFrXB(5>{#H1RsMO?-Z&5BS9A)FyZ2`8vRS6s}^py2`P3?K7PH`D=lSlOTe)ScO_=-?))|3a>D$p(zj)fb$f(&ulB3+8$+z&;rb%h zcGI%jq{lkm5QM$78BgIkA}Kzejd=3f{IeL(dXqRc&+Ha1JV z>Ri^~kbgBEKVucrzf7pO-Z{luYAu&#ZKfOt-!3NH{6!u?Opsr1diovqaoI3s#@n^rdY;grxm2hKU>E0U9tojtJ|-J` zQAe}&3BQ2wWM;{acPyctWrttrT!`&`%$(y2r?LXeC_eEFibL68?`oo$xKTf8HxOHJ zSc6;>G1?6c#3GtAzn{f|JMRCZ2CqDxL4exwtn^)nhOsCxHt7b_N8{SB!N2IK!lR_}uqOH;G2D9HsG8D(O!L&k?;>wyh! z2b!8i%k7@kWU%rHNe+&vHn)Q}-?fwUFR({XEH%-90r!l#ofL0-dU~$6uWKm?6WPfw@7<0fPowH;+@>RF1uKuI(^2dsFn9F}_{+n6&Z75;zATM@Z+EOs4dZ?-7A;rF8Ru$C?M@>5sdW($h zJsV|QQNCrZ?`c*ND|7RKKf2SrhFMbG)@|O&+smOndc3z4j-Q*Ho{9;D45;|82`u97OO!NEq-yb5;K2w0POmoz4 zAvSzMtA1>q9dLop^bVd$>jUYvjx`9VdZbTu0k%}*El$&BexVc}rejrhbL3`SC_jRL*n|134?G1<>O#Ray@J`6nA|2G(z#n}{bZqEPjMOn1TS&K}r27{yJv!p2QE@j|@XK_%F z*G2mm!{(y$_iBZ+^nM0J@Mh~fRUfxuG`C%4i%#eZ(?rqTF>+`$XY8^OG`7?c8@l3H zZ?(_C^>Ch(W9s91^Pb;r0lUp=1bt~D$F@O*ren+HrK5=w5<{}-XLFuRsoe^mzc82I zFMXf<@5j^Qp?}0LaKJ-2j^5;FE{H+33RJ{Z#BnPC!#&2Q{pd9K8WxXiM3~H2cFvU) zbSBwways4ah43BHTokU0$^ozEmd~b#G|UTxI?v(L*9U|2zk2m6txpIDud3Sv+Ptj2 z3a`?34vOgtZy-D48CUo zU~>uNS^%~^5uPn)%SMhJ-efZV+W)r;K%E|J>Pat3TWrV&!Bo5+arjSeT!E9}5?~V|uS9xym}*rq@*%dT|FL z;6nsJZVfRNBwhoSY#`AU=W(N8R~B{DR>xZ!pC6B>-mcD$HBrO-^!_lqPQ()Lh}q5? zOGsqX=ILpVYb^i9+0mX_b;kqQ9l`ui!6p?swG2_M;z6gc$DDn3Kt@qQA{sZ=NAJPt zbSY@ST|KKfjSA&_?B9fknyovY#^-LnG&b_K&g4BB>wzlSvazMliNQQSbLam~22z53 zuNIu-gole4R=hEq$GNSfDvIFO%mMvW-!BfBO?0-HJnRQH;Me<0F0{>_DcopALt{Mx zO+7PAUR3FZXRMbmx`9r5O854m0RzCk`%@4Q4^z|<-g>K`wy<&&TJ?u<{<#o zJL6w&3g1C!WlIfLSl@Y+@etbA5WhDcikPoavK?u08WBUA_#^f>9V1!Ltk=b~>pl{o zmxSSZUYzAEeZ4nT-8zrAN}j4|0^q-*z^98(P{evmq6`U8F@H#$2>G+Jpb=Q8WyAyZ z@xDJoPFSj$xW)QHP$@;lJz0uf;|X^EuzC0T{f|!rN%w}G*}*+p{n`o1iu%LFEIY@N zGig1xCx77H0lWET2u4w>5UYP)9(0V^pNnwWbi42lr8bgyU1*roB{C#6_0`zpbLM=MCxW0;Bl#T&WoD9!QA7ZZz%!DL+C8K*`r zT!!%_S`2(1nLoYB=j^^Xn^*oTbtTX8eR?w|HUw;ClKGGzgIo+8bSe6$(CbR*d|PXx z^x?G@1S2)MbUtDcXj#mtP7*j?%9<$R=Eg)Xsq5lVXKlGNCuTUMz}BVh0?{%0qNGpR z;qi;B5Wb>>R$Z#xUjf7aGo>G><>>*y>Txv4cakp(zg-b zK)Vx9=&vV~if8#4y3auWEA!5$<=xZO^;#9%^6||2R9=Ws!x~RfC8vJ?WoM6lj)E?o z6a*mmAWFU>uO{)7Q8w_r*lswcMNBNPcj~x|S#$Z%j!G!tCu>$|bj0G{h?HOv4l_wo zYk7_G5oKM5y{_qIEVe4UUAMwu%7k=6DQ8mQ7SDIqR*fDPr?MVn4jc@S4xr{Zz!ba? zLnArt??B)_iwZijd-Ikr{!L53w@iO6j`O?0v%@#%tCPexNWB}ySL?~gQ@8(Qt`h5H z#Z`R=OX?xQL2hrkT2k+%_|2=iY~yzK;`uc%`F19Crqd}Fh1i!0kkWZ0wQTdq5EE^CdxE=&aMyVCvsCR384oXOk_W<7mQ>JnN#~Dt?MVI0 zP|g-T9Jsg}`^TExVRj$)BLx}E85QP2$fZFKyQOJ)aLaj5ZFxE0&3@kEKOqeDi0dd6 zkms4CQn9MN;Koa6%H?Yl^RG(6;{@>8N>_Yiw8xtq32_lh_S?k;*$4Z1$dU`tnW)WU zilXun;_?>D7SI-H26x1VwYzM5yjYU|k}tx}USUGo4LV+^{X-An1=zku{-DBqk!)_kI3rOzT7FarkxaCElfZA+!c5F#Uqc1(F z*mi5c(IIJmj(PnI*<#8rRW?$W2khF47ELfI^K}USXinq!(m%!NIoJgNoUb%2Rw|xA znY&43)3bZj?z6>*MMykF39YGHhTZ|ZOh%9#V+!q6kQ*+>Z)E=e(-EdAd4QTS;K~Qk13zCzQJ5sC>oh_z-bP*R{t`BIC}#AJeZ}Wq7nCdj301v+ z-`Nc<*v4P-+$R~dlM8&jvuMImLn#tV&icGz9nYMIGB7)qLA8DKsrS*dkO5Sq5eq=5 zPNN_sD$w9hw?|z2KOP0Hh~i4(Bmu-uTf$A3IaZ_D={A3l4f{}3-VV!g3;q&10)X@& z?Y~u|BoKRfw>u>}wNB2fiB$HvAPNq@ei z7=u_I12yzjB*q9=ngi(@&4~xI{yHv*HDN9+f5~(^d-VpOz)MFM`}Xg<62chEl#b2V zFLjwmKRsSzxyCUPaTQcU;L1!qUKn3Cmm4AKXA)E%60-b*LNf`1=-dlI-+Bp>NKx!1 zo|B49e9`W`h37>5=2c*SK3D$}zY}MWx{MLO1miN7QtP$#+}342qSf#y9+d6H+Nzwq zCf>Y0n3Tqoh_j_?L+z^r_pryN3K^^IJL4-RbFSLVONL^^?%-CX|AdRlXfz_4S0(gl zTO>xxxf>T<+e{or3V7nNS!^%Pf?#xDm$fjyQGo_iJ({-*-H1jLUP zN~$O;<#%F5840+bdo>0~kem%05)eGrE1i#R8L zDt_G>@z|CPGAPGI+|Q4*7E(6E6-6Eujq%l`>VT#a&As7{ewMs#S4(nVk-GNUbHpYM-28L^Yy#e+#6gZ^G`~I#ZKJ zT3s&I4L?$xyLG~cj~5P|n>*MB-xZ5_fyScmw(M!piMi(tzYONk=8l^I2j`ay$zbUU zgQ*wEp#rGq^UWR=35+~?)W7dH7WZT3o-PBgdA&{~O&KZ;pNmZ^W#5_4vwRs8;9pB^ zV`{(H)o=Jdi?!*?vt!5&`x*{TngQx0C95k+3-Ub;Xqe66*qH;RdQQZLhJzE`Q~SFz zDnSX!b|fDiTVv{W0)~?GPw+04SR`2Ezw4*YlhnFizqLMF3o361VfmaSChPY+$mx)+ z`d;U$Nj`=kpNb`yLIaa4o$0#`v%7y6l~bYA24@u0x{^EMU~+vkGNnk(?tfI?f*OTz zzlop1*IPvHzV#IHsu|&o^x}JAy!1PbjEz@RO=apj8XtofCllz}<415e!$Nhw-{3Ps zYbLk%>E48!B*aUV*0z7Sc$3L!#()r~)3&_;pE%OGomRn3frj_yV@oI#+249ea%U{}Th9miB%kdn zb$`sIbfjMqG+(HEF#CZKhaP=nC5EDz zYxW1nD=4y^kysy^)j?_Y)9-5Q4E+|J1?|BtKcJJpBCidssVCvpcCmz0He8|nz8Ysw z)AkDJTNod^TXe*P+spzscxQtT^L?|){&aQr?WBH%(n9Pcjcq9WeHX!S?KvMRx5HqB zowYnx&)QrkSw^H=QNr{}H}nQ{Y%xBd?0C`cftw#E!;i&mpq81w`WXGMl&{tRT2v=# z&)0%2^gUJrsh8CMfB^k(NEO$)zW7>?rD&W*b=Sd=jy%qDcEE$;DDz@9P%=C^nk>iE zf1Q_G6tw{l#q02}yqfF|aHu534c0X^oFV0A?}A^B{_))!h~od+hm8VHJOIFvqn}|X zJOLpSN@zLvOKWUkQ5QS`_&t{>9f*l@{tbz};^2>vwes7q-^>Gx$${F=>jzy2$ltlT zeW!pw{ROCJs9w~(I%n&>pq1h*bQK1 zk-8*~t4-M`t*W0;Kv2Ic6;!r1&3~S#HEY_^(1$rg=hEL>rjF~~KYl(52@C6!Vy?4w zcm2MI;e!)&`6?;R%E#Y(HI zoDXyGSVk$P*-w8|>QY6;vDk7g&_0&L!~R>#Z)twc|4klaD8JdQt`78DF@DC~G4MSn z&G9}rRa+uBILF9c6~M3BJqQ_vvSgAHYzd8*78Wm!&<#grQ}tW%og&Qv$+QHt3+bu) zM(ESdZXDCq;(SkprpP`tT;I0ZMFGKi!o_=}PRV*s4q+L)w7LvwI(5b^iVY=`1eD6 zqaC&LOxZrc*v63Ol7vDF-2jI*X4~DtboS=f7V~S9Cq!>Z%=UN3m$vDTdnI9!kX@VU zJzhK;AbNetHVMxM6nx0o^=N3IYNIlQ6CdE=iYlYSD95;?{>g;rPS~L;F*Oi3e6*ib z3fsP1cTC+;CO5!|zkn@pqmv(EDdUcMb#o#!qvU8~4RTnWRj>Qhy9PRQ^tnA4)V|1p z@qC~85-#GJMybE4ZDvF5tAC;O^A`avFZCmVKqJ8q6szfho9l;iuB*0Y5mYu1lc;b}WK8qd zN~gS2s5+OsSlCpyOJ=Wn6 z=~tb;_q@}>_4a(QuEt6MHuZ*FWz6eS(Qc^*JL|*QX2{U)hrnUS3S65Z2GHeKu!|~T zI!>ZU4-K{BD8o@cE)aw+Eq{`j+lRKe_DG0*bHe^5)J=_E&muqL?#F zG4iv;O7TFkm7R%qRXSoI7R%uGzr+LcSSewKE_NJ?y6-fXYZwG69cj*mM1y}oys6fm z*G(}In0B|~+{J>Vu=bGU1@kd*DG6ZFYSkn3T*UiQHh$1uYnRg13aU&!S59i))3g|q z_{5uP{PZtZ)I^x6FwW^ z@0RaTEJg=qcKg(_|53520;boKBz{TZ7Y$1Dxj45x%95xWiycXl@InBM)R_`U8qQc^ zL?#y!^3J)&nm3#5#v#II8pi$*br_9s1F%yNyuDCQDdLB|>VNm-N2?#RsCSROBIJ37 z>CInUDd_pP=xmWWol~A`$cei*jM?i+fPM7h@TEx(JF`qZg{e3lHO~(C#xLPYATbj~=@!k8V zJ(tv%_->xBA%w(3$wJwvr=tWiv($W&hFUcG3Q&tSAdnu-`3-w25K93L1 z9Y}Gf%REc4E%!DMJF1elj+f6C7Ax^pQZM~Aw84JHV@Q> z{cJa&m&4yUuYzl|^M78J_YAm?#JHrn5vgWqtE3+YBh`#l*1BNp{0xf5^E^QVv5wIt zcaUa)bmr}QKsE6op_vEz^QY;ze-7#|u7&tCnJuSaW)8Y4e6J_0Tx}$)^wQmEg}yVm zuSe;~)H&{>CHTlrLLu_RqS?_Sz z?>J2NJrQ;V@6OGGd^Q0FUUe_Gx>_%F`L}cLy`BC; z@?QtJ_eN5`mAii*A1y7dVyY9g%imzV`ZWlg2=(5hjpG0yB}l(|zn~V>58_s16yG2= zlW%+u3X`=r$Oa3w&;Y6cF*-`h(z`;JA;R@$`gH81H$OVtkjMujal`-7E`i|xH-aD$ zjX%d`&j^{)N{Cekmv!Y+ULFlH>X76A_@)JT#g7Y%Kp-dS&ay+yb3uN7?H%h1v?s0y zYsPd=<)UiicHQgJA*>Nn(-vJ^scHb^BPZGUU2Z5{o_@X6%ZEJ-JTm5p_ox4Hjcv#d z16lvvwS+u&h{QUVvQ=PMmsYVV2`MQfxO3!;%$(uRYYJ*;z@ww(Eow$o10Xpsa!3Yu zz%nvaX~2}y>Tq_UiDT>LRB~!Yo+6Sf1wMo2#+UxRhF)w_ug&?7^>c?a`qzg53sY@KbX zan3&YKaP`M=MZjH2L_@!u5`$N&DBu2xs?deX@+(SUlO9k4~aTMI8S%7E!oxz?#HY` zq1UQb?S{x$IIn`y<4v_WTrOMEst=$`7XELb=f|K&)0@fC_)j?Jot|qXSn*5PerWL? zfFFuIuSdcyCq5Uq_;4S22u^q>T#nc~PiGkXt%=6p4;$L8$O*H{t@fs4Ls=}M5&=TlXSW^{Yb0SW9Da}{i^kV$B|`g-{hX} zA6pS!NlLy-ws5ivHBpA;y7Oy{`ww^?K(N;dr80IC&|25V%|h_#Bj$Y;@{g+0rOcu! z0TD_gr3uyCiV+}FHCyOhCKJ)2R3@tUbyxn>)%gbKpp0z>y5l}SkrN~Fq(nJh@VLGG zuIb%$;NA}7l3P_pUybX8S(D48E=>GlLB^r&!Qb(N6P+K7M=-I})_MFXByBk)%l8)N zBb`#1XmX)tLkI-o=|6x>Irv1AaT>5Eh@X#dK;Mq}!Ta~pnz*hWqf&1}@o70TV01nn zcv`i-YH5B1{uer{E%TN2OBLHBf^4n3S0YTgUE)W4h&5h&!Oo}XPs8S?w9WrQpaF3) z;k>q!#_k;x3+QHEF^VdG7!8-<$L2t82EGZ}H$EVFu=4-Y)nDT{u9RE{S z95fwBXji&^g!ARIR8&TtN2^pZ7-EVt|9~K`0deK^1Zbeq{q7C7vUhr<=gk5;lQ{tr z`0&R3j-R+?Wfi4Za9Zvrv-PuO{5JplerwCgzMFDq(3_MBp?41tF5C`*W^_3Q%#6bx zOX}34>#YXQ5}7(sHw6V|$JN^G)EHx&5$61myYj(pyre$w#z@FRSE(KlDCapgJC;oc zx|TM$8pbNq`-Zi}#dj_<#`7O5Ll1(TS_)teD^-t0sJLV7SoekX z`b^|VSd2#+d)rzI`w+Ck8uAZEa4}J6$ejTGl@*R30wRb0_NVWe>tIW={1HbLaE&>3 zjnlSWO`_?wic{g(I@3~{v4$u5D!i#*k{t1_nF3wBRhu$bD}fLI#1p070eB@~Fg(Y) z6OdzWx*G)uVo^W;`{M3O0I8opuemCGA?KDi?8tbS4svBd|A=-xfBV(L=L?S(?pVsG zzou{28Tdko%LollU_;(F7BYYa+H$3U9WAE+3@Q5o$y}N}Nnc;w7n%f7{f@#9N_Tx6 zFEeG{Gm8vOF*ll@f6ux{nezGOY*q2ve=`@3HKHYnxNJ|V*(xMY=`bzy$w<7iOf9Ol z|4ifSKFXdF;O!V650u5R^+^d3HwwCm2uh$4abeBkFej-arSQ#?Z{BVDx~pY{db-L3 z{`s^L43ZgxP>`RcfegjF8=X?QJqYP|BOkL0H_NCA@BS~a=`R4>OHOnP|4Rc4Q*t#Q zo|unc5$jyU@1$?6bG2C4C$%gn(!36fx3D}?G!9kSLKNZM$=7K2kC*_=4p9(vt4~p0 z05Bv#Boczntw;Swq|A5k1!>JR(&8aCCdAq4aKn5i+V9=bsc2qVSvlA~K<8;j+C5YM zyh86Lo0$UhcA@P=nqS&)JGf#MWm3m!M-DYRzDRHUn^fSyvonYORGwdGWW3Q^WYHk; zhk5EIDHYJ6tqeC;b}|Q;c)Vu(R5;z5!Y)EyT^iqQDuj#VW27_vhWb}E01V~ct0v<| z9zE>icRCd_cybi5Fi^+dk1itRp1&1FDapoi?+JG`eZJ$UWIR`Sk=+(rK}9mdBM#@R z9}fSe?;%oRCelGMDa}U%wx??R)%B zw@YyAdOoE#@H!LIAq`~lIusBpfm3~NDDW`R%}~Ukj7vx#!9P%Z>`OVM|@`9x%i&i{O^2~GQN0W583%Ih+n`Z;7 zAoq6q3-Do%nvH@VkufBX&^T=tyBphTco2`;48aGT_LI7u`4efHdkfuBI~^Ro(x*=< z|KG@TKLhT1%DKA#AO2IZdKKIJAO6GIh+$=GL)?NvHe2#Wj(hZobB#s^ceguJBl z6zA=UL@SBkR||N?=5iJr1dy(f7!?}g&EElzcAPY@rb9}uldl<%(!|Eq<9oGliV9=ETGnLXeP-$4gQGFxBZ9@x;1bEdk>G zcze7U4|s>Uj>o_W-Y`v~Jvc*lFoj1&k-B4d^KBYzPgSA-*s9wOSreP8{S3LQ^2C|t zP<_88-T|HtQ9&q?p+U#|Oi7m`-e(Znf9%nA+;=665Tmbhk)H5qFmR+fCOK#?%P zU+0plPZx3~JEjC29r4ly9(eYK5Nw)~L0=Ex7n(+TZ(xjL=1Z(szoLkALxMo$$Ai2v zH8tE18=>?;#K*U$FLZI2mg*-YMi~UKl$4dYb{v4BWmntXf7I** z4TGY7$fl+hVtkVOnNEaq6u;ENZRgk8Oq-5CdZFYW_X+m-UY{$gIyT0u*E|K4)mXx! zw!8l>!b&UNc}DJyRxY5*NS0O9=1F?co9oW<9zr{l4(2mc1B8Kv{G_mQbb&XJjX+;M zhQ|g$d%$R8bF1|^~Lr0u0wFc^kRcm6# zf7hZ$p}+z#t4m->$pT&#nu?|R+vuL!$H&L_ zFL+IspBdKEt0jv=$yO_)0@JhSY{!^E7mI_}Lz|B}X7eUOQMz4Mxu!K?QDH@prY9C~ z?`qr43n^^wnlzbc+9k$1+X{%Fb-F}mirmqioCZzcf^q8Vd^8FqMG_6ZoHkA}3Ig00 zS&;AoT9gxN(?GqrG*p+baS4knD-FLHNc_XSDJqo_(cEJYh}8l%1Vu zL|L#e+ZR3eLGF`js$-{tn_*1un{!!fUi;J^hSKos&grY z#%Fw_716QIlr&I5qMq0}G*(ZEs5L`tPlLgiTyOyRY}A}SrLF)udhJroU!$O1&&s<# z66qs~jJT|Pt2v_!M4+W;R_WFp{B4#^MNW{E5skLx%os6i!Q^M zl594?C+I27gbMLr2|*7+n!gU7dpTh3ajfS|Id_ZqnO5u0Qu*D$m#uM<>J$byr}7TN zQk@fF7ULLk{BGS&(M?{$H^moWEDiCBEENW`|57&mSrRyGnG17jg7yT{kS{PsRL4qA z^EgYbw2eFM=Mq-Dyx|rYqCYH0lCGmFVB*hgv5>hzgdV?IezXtE-`dcN)3 z)hZEWvgLwUCqU(!(O8#4bBF2N1Q%=W!nU#qMcj~m<9FPPJ5wg0C+=>evBX3~&vkm= zBNaf|yH!*p*%Y{@R!t(c{uMK`ndm$(x@V1Q7pV7sRer`*Mi{`5Td#5GALH|Kam!A3 z&Y<}#2C`J)h6=Wdr2Ge`L=BA?>o-T$ocUZ3Sb~0#fG_%zR3n|Yxp-ZPA(=fqJaF_AI_b(Z9mE}2Lfd9G?GojL_y^tj}bAOgw=h_%H4BwkUFM7kQ zLQ@X>pJ7_Xf85u=JNnsUyqqCZ5NmMOrYO;^Q(&j`zk7qOePe& zxFL#luC<*(XjAw&#YrHn$T#_R28X)asVYx-xb(p7+y3}3TVe|fh>ztTeWn+pQh96c|I40HqNDyE8g^Y*xj!3zrt3nyE? zZ7s;UbzEy2qtv1Q!b4r>x&-^dF9r<&Tok%msV}9nl~Z};xY;+K7)IQ%2x&{0Y8Bwx5-oIQ-`Um1;HdQ8M>*J%AxF zLGGe;To1dULP4t$ADdD=oEP@^3LX+8^yHpt9>=i6y87u9%6CC1!cL7brP4B{>&}G1 z*^$<}LX!Dl2w>-QsSssg_%iPdHK&24&Tf|VBeaSHV3im-hmGcYnhipfMui~0P0$H0 z4?V&A0SHz~PmPkexAJ8vrg$y#Na|10uC=?$8?%XGGAFm+C$d~(T;dTuqmIi12jZg5jcth89;=EgyP zcfactOVt9+Jhw1eIeg>%j>vQ_w|8mWjxtxCTtMnYH&ivVeaQ41$5*SglzkBHv|;lb+g33o51}|%yB^3Y=ocrgKT0k3!#iMYY`nyTE5ErqP^*?_%M?G zBCD(L&!riQ0f2`@SDSm2{zB4(R4v(lKZO#KzWPnr>0d_*z=o3eA3qzfI|eJFIqn+f zt;02XQI8a+XzHTNEU_ICgM74IT{)Y+i_I&w+SF|!?OH-dv5 zyp;wey8pGS7UcEzJo1qYpF;lgYVrShxRhuyJ05M7*yWo}JRypsF5U4>c(6Vz|0-5V zpPNn^htywg@HC11&QD8sKU_)lI1$eq&ljZt@$ogBvS?KotC2=Ro_tCvL4h(K4}ho8 zf@5fNa#gCBs!0FMc;Z?8;-&B=U3VNeZO#V@nvq|&--(KfA@19aO~N~b?$4OYrWRZ_SUfT&!y)LNPA&6o-dlO zYS>ndR|os&bC=C!hkWw6Bz!Ly!@ zYQEWiw4jIW!ZsoN>D)*~?dvD`E?RLZv3^dYd<*y(##wde!|G{=6MbjHv8OJ#?b>mT*rFED^6jTS!Fbp^(@FO}EZ9o=sTh%W1oi$0*kJzwbZ zA`Y84iv-^pU<1$2)4q*0&K$=6;=vEjRqBTxb>BTsk@2LbkTgw>@s>EQw%lJF(H#sc zW_AituSwhtGU(o+#01T>>eaF7H=LyiK5_0ZTsqS9fMXQyH5l>}Z(n8O`a@<4CZhis z>r60AdeOK^KG>}bI$FdKHI8}KEclb>Y=NT%a`$v~EkRu4oPO@j8$Z#eq^V3Bm`Uq< z?4X;K5Qnn3^pEmdBQ6XFJ#Ip=Z))8SgqPd>-!r}Ied(o15qC7;1bz(-o36=!OkE@! zx^T=0x<6*nB%A>l7;)O3xyW!UW?YTLZ6zmXC`&ol7CV)3;vZ8g`QouI>_O*o15@0d z*(8r;2~3vKRoVUxx55hXG_bM=NJ-UAc>JGc(;NmIxaj!6s<5cARL-58>Y~n+(qw&P z-*W_HOZ@XAQn2pVud)~p{svc(n9A(hriKk6Up8BlpYFxGc{R--G{pReU`QYpI$}5p zwTbP#f8g=6xyR+9a(lY0>Fg*4X*y~=-*+Q#S97rMkZF3Dw+3>ZtL2GjiobD-qZLxn zH|2iTZ0MV~=Y#80jqNbp{CiBnZ6@gf#I9#8?6I!z$}u(T|3~cpx#RdX&o5kxyDVut zh6{>2&==IN->DI>O4HBX|Y{B}%h-w3Nyl{$!^FvXzJ3)}g)lI9gMb zU5VrVgP|w*ZJozW$*i@Vsm$V3g40n~Ow{4)-+$G`ja9RPkh&k7D3@;+azarXtddrj z##RpjtG(MYP}sc43&D~q#KnwZn}7F7M0HBqk}%ng#vf+=)2$F2-;79{IgauivYoF` ziuza_UY|gX%)X<5_wMH>N>wMu<&_P=UjlrgQMZ~#ye}5p8fj9}wzY!%-lf!U{%*{n z;>EcAz3v)tb2?{w?Ae>AT$=O9*JbwpJ^mS4nHj=Nfr7W1>>}N4eBp7A_w2S>WPgti zGBaP#EeHk&S;BP6$qlr$*l&cBp}icucD%J+@+iafD3_%IASKTov7qtjP{t{n_2R-? zGsi@|<-+md!3aMi@-W1Y8hqkgOXPdW@ zi^cbKtIK?gsw9TdxH$b;nJgtLyBdLwcAQ9D8R$~b(%#0!+GGAsH-@Pb=;#+WK z*JK13^DPfr@msFqAmr06ZWmA22nvYLw!!-Rpqn=4(}hXO!Podb&w~Q=jzRdhr;V^+ z3JRNEf0jr7bZ6jc>)!sdQ_bAx26*ST6I#ji!_CPEl^5kY8aNFFX+5&>Pwg^e8YH{D z$iu|MsC=Q>RHY1NFzUiZ8D0Jh0`p4RbOle%Ho26Q$T^yW|9rQ!+$2{4hYOxj$2>!P zcY9a?Ky>F=Nr=z_T%vwMu%cT@V$jV?=bkr*YMPL}AS_u)NJxl&2LH+Mvi~hhTbC(Q zA?Icc1#eiFF?%uUVU*?tRXjgrjQzO2oJCV?&c)gS&;vPQMiWFzVk{LXXaLk4gIHiXxCaPXzU>K2lmo7kshIUew5A#*QhhXJ)M zkr~3*($bP!YJKk9`S(|U47q}MY`v4BVQtBLLZDtmf`z@jaxR*&6qI850%Zz3KVHms z3r8cQTzQ||=Jw>@)8ps&4k4WUCoGTd#@^S1`;AXX3(X`2DD#&OA(z1%&HQ{YIbzJzK6HvPt5dlk;(Jrj7h`wVd^;S(j@vpnaoB7>b#?+T7)8R#dIAHR_`|k2{ZM3-bg1VqqKy}D}fH6QFjbPXka19WJd_+ zfJ7x%4z(;rgO8c7*VqAYsHQ~MVQG25zp3x}g89D~m^=sX#{D;{&yHoVZ;44N5H?+$ zH(r#)+98mzi}tcWO_zMG(>G_dt>Punx3&+Sc6CHlYa*X=5=zL{oe0)zT%U?cOQ$MQ zYIsq!x@9pN3VD6>V`(@lFOyXn__UCe&6`Y9pSx%M(|U_LA|n9Aa(?AHJfTAJvVz?D z%eY6!F^{}b*$evSCKHF*1Oy4uehmf8C!i<7Zo-LGkPLg+LUJXs6G{BGF{sBb359@4 zXYFsI|B&}#@3YVN&`hLr`ACar)Bh6==+4NLzN8l_c8gpeQ!?ai3In?XzXy^NSV5la zVljXEC7k`OV_IZ!eu=x)sHK8_#5Qx#4k14g%|nMMzK4_^PGhdsI4f7@N?9LK%aP8_ zC#U7~o}_mljGtOV`y+2n?w#ACQWm3=SUA}dm&!8^7FiO6uiAh9d-6ALm8^|CXt094 z9JW)|I}!Pr6keK{{`Ngv+IVhUp|jq$?|$`xUkK#%ly*6gDVy%oWvjD+fj=d;J3HH% zYRTlJ*Y!<1s!UT!KZd5DVqTusKP*+W0?~Q4g4L{>cF;!UT15C|mZT)5$h!9gF9sM|$j`myF;>sLL z(Wk5+6E%vZtwJCX{>;K5HwfG@zt|FXI3*F+om$SPD2j|jy6g-sH4hA+<}UGTJ6RJ! zogfO^F%{-e?5m>f$+c$wWm( z#c!Fa9?vl`x!R>m{h~zzNikD}(45i94^Zn5vEU3apAzKi>Kn?e^$ufynes`+#?R^$ z{Z$XIlCrYz6ea8E@<*cgb(9e#?m{ngTzNqEE-^?~Pt&*fiBEa8z$>@&oFW1h8(Z5X~D zN{CbZLT0DN+`|MdyFeY|ceB+%93K!7K*??SLt|D0im(He8*~LOgw-!4swAF&S*Ew;L z5)vfKN@+yA_oqs>Wu1zuBP2>L$7 z{S0|w>eu5bmW2bKTfGv*gg=kwTntFLSeJ>PB375ExhiNs%~m}MLYN+vVj{%h>ta`9 z`gsmLF|FOyg(i;#4W5tUq#%~*@4lqrL2W!9^CwqtvYK?2l$3a`4>aj?N@5COv?cFr z&k0gBS-83F9PX8spYu9C=cT7gvgnb2In!_c#i3+b&`W|#?LDfUJ0u; zR|T}FZh9jxuQx(M?UON7cGD)e?3Qo#o4&1#I$Z71Q8g;MxSV|8W>P;&6&x`OiqiwP zN(=XS4pfKEHX0FjhL83K$qeIVE&tzeg`C{@UOi;NSm-!Ux3(-DE{eF5yTdmhFrl;v zmZcEXe4VpN;Kn{9vyrEOm_6cmwrxIFQWMt4eO8Io6BsQbIGZX0)s)lO7fkfQnej>a z^Qu0)lg;}*y8XWZNmEtS;KWEGX`#zvrWRYB%WBk@7wBkj5vic`zV@&$9Zr}=-j2-m z=47o-VsSZ0!?;Oc7&yvBW%`d zQ2qtFLyTK(ne??N6sb=aVI&i`AbQCN{mFwbDT@AV)BtYr?{qwJ=2d*4$c8YPmQ=; zP_4CrV--Zu;k_3VOeSrXbzkVucT-v0cQ$lJ%1x;8doLutAZb z993nR%)fWI%o1D=EISMMEB1qv8-GWrx7xTb@d7X3*<^5ePm1BTCWkqWFk==f@l0636YpD>msA>1-@Q=L!XFSwz-fH|n)dAANsaMlJ zJn(c@Oyc1B)|NV?XQ+7xj!jM_X9DN&n3}zL^G4HAiY;o~qvW@j*nZQ&FCX#3Jkj_3 z=RdISMouavFn}vqAk?PC8AYUG=vc_upY?kakPHKLplp`YuYTj*A7CJu>!eGk|H{kB zwV7w@h$FSI5xd0$+VG(qyoeZUkigRRj*0YmwRseZj&V?&Jce}V;Zq7e755swa#BBC zyK%Ou;NRJs)9@{~b=66LhD|hzl5;IZEkEog)LWI8_5Wq?S5osXg-eei!l;b&UL1%= z83#)D!mn0p#0yVWnxg_GJ_J}2K0o>zSfFm9SeKq@_5W_H+Pk;-Ro)uj{Z}gy*hhvM zL^?|CH#E9$s?3?5DOv3%Mc2v=DvKz+9032GpUar7eSCXyvFD+0#5n~hdH4CN$j5Cj zn4%hdVred%L-32Hs;q!b$!ATSyP&v{t<&{Z6tqj9oz#+O+F|ANtKRpj5->*FudHx3 zBd(8@T%tzU8)8-ejzxvtuK0?JZKoM;53v!4_3!2kXy_!oelW*?7%e2>sLXNV8?n0x zR+jOPU8A6EBbF)){@EbPL*?tLUnzk_6|ergo4UjS$m5-dE-}S)Ezj%z7tc7S>=M3} zjzU=g=$%&ZChE|DV;Ygj5cXu^I;5nV>PJgkJp$R!eVTt;N!H3H*`MLtY@QyfSkR+a z1}^5)ZdK*;-8;)!-69y1huIw#edb7m`TcpFzUI;9+`lsLh$xP!N5tnO|CPjjA-t0@ zQ%%I)H2i7UKiCIi+#Vum#)oU7?ya9G;Mu_wbqG#}QmjMYVRb*lt5VNi`m6vk%_+Bf zeAQh5;*4Y2ndK-<3(qnao=@Y1kAEJRi4*h0ZR>vW|C-}jK}K_ZHPOA5JY*R2fT=Q^ zFNK&5m-?XQ9QQDrLB8AxD-&@1`kT(A*eXe%YVjFh`>hN6Su=e?H$~u_*=$uxpDoXfGUa#}!&jeFd(zzUd>4_AI8ljj_bG`+qC|B68>dH!-E+$w=>v zv{eh`)e9zHjnT{b%5u|z?X0B~w}E(nf$(!|U%z?oh;?kgnN`|=JtLpY|`%{Gdyfh2#2 z`=?S1&G5LGYs@!`wjWwJdS_&4C~XlEaHlOIFGs7dnyHird%R+S-zd1t)dHt zK#;B4EzrP6PrrkorHvLXOMipcayuW&;4yCNXdb7>S$J|Lmpg`Seix0)Uz4eJ2K*IT zFDh8B`UEAE?0!{i+A%E6!X20YAjpO-Z52)KZyurc|L!8&)|lJX(+BUI#s%=r0jThU z>^o~7v`@JiK-{6JwJ>KFJ||8Sl;O3z%E(Cbv5A(i+Kk+j@R}Z=k8KNT;y&mEELiNz zZVIN?dd|Ce;O+9d&UX2Y)*Q_Pis^ggCZ|j3NJpE7dj}XdGjxscT|1Dutyev_P}V~3 zUvokiR~H4l0WuYMqy*Z_leo%xzvp1!Vui7{UA^q2AH=%`G)HT!-=P7`W-#ELu7{)z z0GKOe_m3xe1V zlt!cjSY?;qvplJGLeil{`E@1C{ga7eM&*1WnbMu0!FfuktI!Ua{^g>TtgD&;p0Fch zl6-%fhO5Q%`aa!X(V?Wk!X_P48yPo>d285I?gIhjJN!GPeyTy^g|6YvW_<-i;Qg37 z-+9Flt3)vd_C^i6zv+SO04gg+^IBs7OL-4>-3|h5ix9rhW+W537g^+z1cU!IrulO-q%~QV)v}_oIF$@ z`J!(@f=7FiWaB5s%|M2WCW{Dp2l}gUq2&S|vwvp8D2E4R1+g~a-@wmjF^c9$R)NJ# zN|LJK?wDGZ)D)DfGt>nwgGdxRV&#T=zGUVk4u)tt?C6ZeKu+Y!QLe%jPznvRsZa(O ze)E1cPpSodn|iYS7tZ9%KAsORTfRpn_EB&7SzX46wIfR$7ED=k-y_WdjsEeb4QC;J ze26H}V)mn-;WwBl1$#AJ-~+#j$LhBfGi_>)e9j8n2Ik~-_xlb8Zq<4$~USKcYlYLmzJ)mB3=IFoNZY+U~1u=*J$B1)^Q`Ysv;h0wbyL*-O>m7bTCWPBb1cOF|Cg`-?_+?wziEy! z99gVLOu4oapcd~*JQr_Xuw~hB*`b35lz1No(%uu-=Q?Y~h>sLVPDxZL?Lp?lv-!l4 z-fz|O&}ca-td-gF*c&= zC$-QUnG|Pq5O1Z1E;ojkHMI>F%2|G^Ug{5P3k^b?i|DG-2*2rjGD#S&;g&~bE~nUKpV!EoL{pAUJw*%IOn9m>~m zaa+816ZLV>)X~ z#>oju7$fF35AXhiCTPw^!#vgX^=R|B7s*Gpuj>2i@b%mE%4|_qR#y7=@3EgHkoz9G zmIyKdJ&IT?watWD?|=BAy``|}k@)l4Y#%*e2&S!JFzRXY;gAZ=ezqPP<2SlwYw~9V z^Lw@ISHJ)ClLM(R5Nn=kwu~5E(4Z%u4g1&1_{N+3Xp;aufSh_dSND1rYX}%09q4~^ z%yQ9^-LDdw;)Ra!AB++WSeTCP#Yjpm=B$ePaUpd8Njsh{@_M2u1RwJ3#IhV=Ezjxn7 zbCU%26~>EK1m>t_2~6=$h!%Qo*`nB&-(9}!EMbP%x)}8zEqQ+?&YiAHda=%#)viA* z6HQk-kZVwJO@wVt_^--yYj?s06(E~?2qD#VXj`6`$cqabh&T)C6u}WtrJ%qb#3qnc zcweucR_FeOd}nvp@#*23P61NSKx@4fX6CR)Bk0ug^%4aanEu`l)4xZjhW9K3GJe)Y zWK_W&PT}ni1fjLt|L{jiP;r6Rzd3ZJfA;kC>7tRO;3Zj6kWjj@8dKq7@0ssv@LYDbhZ{PpEsNe#oNQ^Y0X*nrg$Rw1wLuuQ-#i_V4~i~I{}g*!E~AzZ-CG34WE zZs8c6pn2BM1E0da;G32qY}UPe)q&#O={T$NQFxZDEFM;WcLP3~>GxK4-cp4>VFh>N z1YWcABN-V%fetFLS2))*Xj(DVq015S1aw7;#BR3pw?oJAe1>mwBw7M5ja^HeuF3F7I0fU;H>1?q>+W-TLu zf4y99SGx0Eb78@e>L?t)@0gsFU9z>S1Ui)$XY1w$~z^EdOKe=5{9dN)2#Rk!=|E4xvmm|x#5 zDx&~Lr=2h<6e7mh@>%EYsS*}NRUwgSbTe!+GS~Xe#cRfa2tJ>1(B0YPw{4bQzdF}#QSlWhY^NYH|D`P`2sihc4$)KZwsT2i~zv5T}RWGjryiVf~H2y zIh$=UQgj}BP{vcwOF{yI;!bS7M~sMIfDKw`^;a&ozSSO?2Ktt<|D_C23v26pst!c> za(mdlH=8c^t9l{JF+7MDLf<|k$i`Hi_m45>nZEp&m+MEhg;=#oA(=1GtR!~Plqdws zO4DT&AX8TTGs|>>3Z+i~`fQ?o4WcCKK^m`k)daVIJwkp2n(tht~rJB;v9 zsvF<^5DVZPXK9t|KKlDzG0G9e*4D*Wlvnnwb#kwf$x?e7OaiH@G*W^2CR(MU}6y zBC52agM?L0skCE|dRU32gwNIha59ti9czM7KUl?JviaNTdd<^`8)Gm8i)1?2_b|~f ziokoVF~Kz8o+t-in#~ z=^1E;K%1R_{AC88a!94TVap-*(tm>fb_~Rl{-Xc?dF(#xQo(=aP)cda1Bv!*EjnG_ zvW06Xsf=%47Fp)@@SdfSLJECq_}S!VY?psxPXEvh{USv#x&6|-L`#bZ#eAly6Z*Q% zaZL^!TM84XHj}^H{Z|km8alg1&!wq+f3lEg-`!z^Vp>U<|GHY>)^0@cI^Wr&cGN9k z{-r}giBhVP)gR{cbd{#E6Jn3)-=aVlD-Op-8cfcEQ_mhj{~A6;iGg!Si`BF=G?;xK zlM`@xk;_XUBXtU1`0n2PS*|UWu=jX$$}j2`(nbbvs0Pjvzg~mp_OG6M`r<3^t*<7L zad8D3!jfy~7}&(gU$1k@QTO`rl>QnGn?mY^%y#dYe@)B(gtBTc{dIy*{j7&|t}~aD zn{e1nh8t_@zPsH-c8Ue+u_T{gtu_DlB3K8&HL(y)tR>Y?HOzz%u8Ph2AIZJ;!VfS4 ztUG2LCMrnqtHWuh5BGQXnjlKsj$N++lz2-8I28Uy>wB4vclWd2?V#ZvMyxYB{}qei zlzI6ma;sLS@12>6k2)x6z`s&=hR6M&Ty_Svo2J3V3y;g?rnrmtJ6+=c(WUmWn3&B_ zCPO;X`_6eR{HnK*aQMUK!|9`w{R5t=*+0}2ujHiRbw}%N%Oc_WhV|fwp<@1j#lnJ5Ve0*9AMp3%thTg-*T0-#o0t#NOBk~40(u(!2kP|JbMZXP| zQWW#MdNpTF!OQ9Fl=yof8Ur4i4?(WUou8jO^Q`H$mouSl>h65s$;-k+|H>Z(_iBo=$qB0ptbVlYp zY_6+^lb$ReOUcxMvhK?eivGqrMk~VqY~t`uztgJfS(^F{^^2l zUmNb94GARblzvN?=zM|%7huE8evf{CASC}xC4l6BbE%DX92hR=o11+`TMQQj7}h8W z+8&c)_L~Up`JXNg9Q4egu1Hl6si9h4_9L3TD%wfP)BjP~=lo-1%f4(Snqi;P5qwxhl z-LiEd#5^T4et8nGJRW_(MY{u8^J1ReD$%UNoN(4TcO#^NVia@-uTVO-$SCG>AP$DnJA}i>AMNR4VMFB&d(`r~isEFo3_tp~J!K8gU zF!dY8UAr;%9q5?MMs$bN#Q=qgu=Ne>f`%;{5MWrL&2qA{&)UZK3|EN|Bw!oaN5saK zxcw_lOHFm`al>d5dq@KS`%My~UrPS@Irl(|t#cq>Gg1Tf z2HtYic81Wk?kl!Sw892dYD1juIiM4qZEAAY{8~hdKuidJke>C4H4wf7>+`@9y1D_>b-{I`)r|*Gj=8 zEt1cEXv8I%W+xoJYuajAspSwkO|3N5TR~2UTzqH;C3<-`+1p+FOPEfKiU53bVUMSd z!+_lGn2dV4i<-baezyV6L;B*q1(y`TD3v+ahZ=Dwp$9)RvJ6lZMCUciN(%(qy!8TJ z6rBmqw0>F&Qa8G#OM*s`xv2zEIqjs37n|M=ekv!u?iYeio*4;u(2_Zt$aBw}Gbr`Q zZ@`)hEr+Q`zj&gTsN?}Yz)u#vsr?snBmt#_r;aSp4xC11aWudxi=jlO#)9Ns7U~GT zUhfeEBW&Os-jK-piH)zD#jzstrun&5kfWhEULC#xiGNf8`{Nl5YPSavh zT;c4wPDyz}P_)iAG@;F$eu#d7`bq&3DNZmfDoj5x;aHxu>KZCvfJhzgU`-3(=_kTF zIw|{*iF_!J-_KgsdIG~YCJ1FB4=rZ^z$fJZ1aDr1$h_{99?~7T#n^Yj-7gwM)$P(U z9b`?_RD%qS`6ap-cqpRrrTLY(8Jd*Psje?VQc|Spd;}wsH6{8_2iSFZkcwTI$WY8r z;|FA7tuQCt;V<;d$d;0|2LVgSat7#4U(N*TY|JCc&kV{resF?D4bb`9_hNYls&*#k zYZbVbxaStI#@*hkod^oQ*G?RmYmFfqEpLWrdoB|-3}N*6RlKHOiY9t$E3mx#$FSrr zRb%HRlnBuep}!wX9E6w*H2v=MjD2(OeV~MWNa^f$+CUd=zOwrY>#1II!M*nL1r7N0 z@FLkEF+kU1F3wS%Nz9BRHYrM}0zunX(hT2PIqxw#O=yM?NCcGKltV)F&3rYp(=~RG zs3#RG+Ldw8n5DAkSTf*TP77s&42VEAA7?T^hj5de0V*cXRo3%QJ!QtizQo)U?eS4( z-dmg->=a#_ylXLsBDFy$yhN4jXvaj)I?H{T)x?gBN7MPWpBbk7e(a?` zUgl#*VL~5z%CflOT=S=aEei#&YdTb0aDis+7&SD) zAvK7Vl`}=sb3v)Q_O+t3g?;$Pn{-7JXxsk38K&R;h!zXY1?wvX#MW4zHKZ8HvY-G^ zZIP>}MwOp^WbLeT3;moZE?5oleA3V1eD5Uk3-&co#;V(FFEaEmKC1XsQA!d5N#?X; zfsc_LuwTn;08%C2`0$c&lis0Cbto!Jn!M10PM|J{BYoK3*@OBf$x8Bfk0WLX_s@5z z%1%Q$cuqroRNQ-_2-1f*Cir#qq+?$c>9VXN(}VRe9lFWeR279(+~_zOkucQd(P7H` z@!{cBLKao+5EBEtr<7@=i+;6nq@1s`M9NhaAkgXErmORP`9+i$6PG^Z@o2t^*lp4L z*j4q||EQ{YGg&(f&|i_GL|xh2UhB{wL(ziD)o=6bb8f_moK7y!+=3uXkjGWnyMlf^ z4z`T5W)KKB%`C{*nCcMof(GV;Ei5})5Fft=HZ)Yc1AU$~dt6?y97_`vNZxva0U=Et zsKUEVhMtP=5_jE%Ur$@l$w;>-72IU((wtq7c2LD>{L)^c8vyx%m#QryTxV^Aa$`%t zr%}kr${DrB-?U!nxxq%wVedYcLe+~i*N%g5A_vQM<}NKweXALl!|N_06a8Ydf9Kj| z3xEM;Pnkz`U?3*I_p(ppwZD~i<(YQp{8Qx&S)mcwzxH7HHUJIKcHvkym|!FTA!^6P zeNz!Z#pwhrL|ldYpOpg<=Kq42mNhlUSu0rGX1pQK`#uP)22LP1rbpIDvTPg!8C=U^ zZ1%y0CO(#hFYpLGEj0?B2nJ&hPr>w`WCte=j6Nm?9l4#ezV1~4QGg9)d=NHSvpDo7m-7(}lqaJ~XN67D9#z;lk=#|>XL4_M1!3>>zm7kCb{OgAi1ChD& z?&l1L`Ze7;&?=8{gOZn`x?jY~Q3aW|{^tpzcxTNJxZ=coDx1xkDE!XD&DT}{=Rl-< zMPky8dcYeXy^@Gw4?76gW{{f5Wt&n_X4qeBrjijG*kLYHXQPet*(i6EJjuR4lXqE! zG?PlT)*9&R#yyl|*r!8?Y#Q80A0rSlCZ&b2Y%b- zw7oESUwC+6leHsPON5vA>D&*KInb=~On=Ab!F#L>ocKz321>XZ#5*FL4YP$Sl&T5; zHS_#eKr0;)8&>?F1(Ah3_8bv11@1)Wb>~1@_{Z_CsSxwcU|jE^(D${kAy?AnI6z(B z!q((*?s@A6F){Z_8Y%(TaKn~LRAhUu$I5Zg}(P1_o4Aog%d*aF;;`12sLtsnTV4qa(cTxWNH=a9F^J~Y+VMQ}O}=OLTWkP?gZfiVZl`gVTm3ir={@6B#mWrzp}{v>sCNgV zTTRtRSD5FvyL6H8H#70~s|?e2O)ncBeO@tJUSVclVX(dzvq-6$U3G=kkapTc3U<15 z#X-wev>Adx-}E}Qr9(xK*)o@H1BSr&{u^S-E_}^%kMHt#(wr=g+P-f$mUHWFYU79$ z|3Y#40>>Zcyi!$h505cBoQc(mqQ1dS9dBhug=^SI;M6{PAv>^`NM4Ltq7ns^qPk(=o``cCFzrP*CXKP)m{wPq6s(TpLhSD&NgFv8P zWnT&b)xWGxU+fnQSZ@v*8~pAiStZ($diiw@uF%0%Njt6q=_z)GbhYfDrD4br zgFKx8+)yX3zJu#pJ+w{)5}=w5RCt;jo*Zc_+y?sl{{Ibt}2xP;`{ajenxbj5hPCe3(F5t?c zOn<4oX!q(7dg;|e1$(L6I0llf3|N_fjSpw%Mo_WJiIc_VEQW-J3J;m@sgP}+$#zk`vRkQL- zg_;gHTywLsMf$zqEbJkEhcv3TCWgOlC_4`INiEcXlH1PaojwZkkRf4V$iMF;o|cjU zWmK|de9oWfFIn!v2hh@USDtdmrB?{>2?wC}azZS}92F@{wSDPf`S%wx257sNlv3!KN%0jcgFl^uAQRuT(L}B z>UdopG%^=^Zw9@NUi{c^qA#d4hp#Qx*_y~GQo#!us|vthSf6KVNy z%>pq4?jQCa-V%QK@s7um#KzM@bM=BbS*ggZy;&-nt4f%U2vK4gLFBh~oh708xy?h4 zbfW)%M>MSM#?Sb}vy@)!RXK`Z32xU$Yf&q}^Q<5Lt_0t20TK&}eh2{qM|Rw5w$$X* zbF_hv{aNQBghA}p_WwJK-4?e5;;0v+JSYA!J{#0a)}8Iy`S{C8zekqn8dJ`jl$Z%S z==H1Hp&k=YhGo$a$vcqY_%5{;p=zf|T<2VtND>p%O~AJ?LyqMd)-0zDn8A&)-+k8C zJrl@6HTR)SQXtu^uP$>r>crJ4S_?Vd+81tnK5#1@7E8M%T7%x^kT_H?#64s?YS7?z7rq$>F5p8Pj5T*Sm=j@jr6y zZwd}8_}1Wx%;UP(b3Mg}W6$IWM=PFcS#Hpybe6}rMU40Nm}-q%)KF)q&vUl)33C^0BnuYqUR7WtYTXoc_$h_hg_ z`(DrKJ3$3I(V4vT2y6Bg0|`@tzZCz+6nms9hiNNc2kH{G>>t$UJym`l3NVBwFMJd1 z4U7*Ir_-^9`k%JisRSMVy&Mp~;qKTIgAFaq?(6I|nYu2qWXMy{hHy!-DUp}<0%{LD zh?q7nkqD$*-RaZn>Rq!s@SC+!mFUejE)l-}j=~ytH4vND_YRSGQgSf1c%~H4K+Tl& zPUFtJ$=&Ow*p9QHu0- zSJzW}3{#@r{RaAsFsaWct&(r&XEQ3%-6DF8XtJiC1~Bk4**?q*B4i3C5lTUqVP^c9 z268SSg=}c&dALTwQSHY;mi%=NiME$e$ia$Llxzyu)q9C{?Sitg_#JULfELmjF5@Ky}He2 z`zb9H1fRe9bODT@DO(V!_%>W8^ojNo$AmQFu_+JfR|Kah{rH2(_2?i<(1b~F75N)r zd6cUEH6vo(a^@E3!&cpS`o#F9-OCkq55KYX{dByjkPE)J*zH_)8aE5r5DM5e6Bl5; zs4-RJm{YBzL);Oto7S-lh}^gR1pl%UA|`v;;{D0e-^9i*F>t`~@x25Be6t#}3cLjj z#D7Ed&;<}epK7QqLFOZI80oB2P-5*mp^`M@kr|?_(O1&-rXPXNN*3bb+-t9kuxp0? z1l|pJ@9Q@1L=vCeJ?KMzV4osAjC+~dO@2(8#xuvvvz3KQ<(jlBLT6KLk=5nH2WNv< zw~XWVg|Q6jFge$Zq4o`sS?@_R;zMRbHLG@U(y{i-2dDyG3ff~5bm_`Z(DP24r=LHJ-fJA z)ai9ELwAo^65%uO!Db8A<|w%2)_>z-baozZ%1Nks!owpY5>^9pnq&|@d_KIRDvs(I zri$Dy+lz1A226e6J8XYy_dlIB-B??tLEa?w{*GDCkig zujaB0`q5MTrV4d@%R7yNM1j*7M9Nw!Th%WUvEz5h~gkGnH*ZKLkxg8H`;-&EY6*2kll|23vO7}xOFhJ@=RSzRH2=n{-RGAfb z;J(S;)=VD!72@s@+HWfZ`S?05qracPH#9}K$ z(Ac0Z*{h+{(&l&V$R{XR8gQvOwuds*J25Pv5_BVdJxW!vs4YtoY3!%@>RwOMr-z~U zdC3cfxm}xm(Tz8tchXYa@2*Ys8JtO<^v|_{+MaZFW10Mie{D*{ONE=S-IXujCXLRP zN>|qW&~l>nSHNuGEc9{|Tl==dZ_wc5U^t+BsQW{`+E<4-6Z3lzyAc>CExJoq7F+A` zU7BJfW3y}E^LWuKv{|;BL)n%f#Pc*s+32bS*qx8!obKxgI{z!Fvgj+X*76qTdzO~Z zrp2Jo7KhAM0y9nexpSGyH~u*jLSpN<>n)$fHI-9_J3jL1q&X;aFv=s#t#CbwPZPsW zXMSjFJC<&K$K|A<6?~mqlsOA)qr8=u-3OcW|7mNJGyNGLq()ZT7R8)(lsrVO1zhkt zUi=AxUF}yEIQBwOMX3^*@dMj5$OC5PHr1ErBs&NF zT^LTNWW9}}0?6fNRcc}VX8S$ZM79os+$be(r(7BzfHtZGNyy=;TJyxsKOS~c4hCLRAO2F;`SGrQ0e(X8`q*!_ zl;PuajT>lbYz*~ns}sjN3xin^X@1@6zh0IB>{Ws#MQF7NEp}MXo9)vpAE^HLUSf%& z#30MtT$5#^B(*P|VP3Qaw1&z%qVLSCMz@-$pjcwkHHNmltztH1Wsbpy%*spYG8KfL z;>Dj$;>CUFnqZ41IqD`p=V!iOHr@PqvZj}iA{ZO68rXew_R7gGk4qPKl?C=aUO=M$ z9H)w(3quM_TDa-J2KV5Wm1{O0QN5)xvbcSbT-YHe`@j)DHh$RVr=XJqkl-8;Ikf1o z6;EX!*Vmd=qyW37IhM=Rki<9K_`t0FhWgCWA9trb}SxK#4vvmQ8<~` zEs0MFyWDjQ?s`Q!(`^aEawq8Pr>KO=@NHAwca{OprHf-3+z(Q7&OEvvCw+|*TSM<@ zQzGU%4Qek)j1m2yhRa#!8(uvl42&lqDKJPK@#N-v)<~$#q|j>v=+JtWGphDrB&?U^ z7sQOZ?~8IyJ#8kT4wj2ogU#X*f6b!DIB9 zT%?_&dfau|KUK&->W&!D{|Ulih64ZL##3`JgluD8K?1i>iqzPD(O4Sj{kV6-i!u?f z<4IR?CO{5Tovb8T1QT(R4M?I%=Gi?%!r{O95e^fFrwGWsSHBYXT@ zvi4q4CKx*Oo#qh#$PDFWH~nOpCyatKsy45$E_9lWXzwvD3?lHUOet9k^#lMwv9_~D zSO=y0P^_vLhiL~Mq4>k7!ngVN^P=BxO&%;!oIazOwb*_ojR@UlQWJ z29a+6At}<#W_vEgp2`#DF{ss(QeB&Qb_?BPMCEoCFBy`IQe3q&()Nr;vgwTmDbYO< z9!q_8R*y|%j4rI_yH@HQTy-vv#lgF+HMeht3B=>88L6fMzhQ?xs26P~l?Jd@lybpp^ zaZbckc&q*Fzt+SzE;|1oZEpb;)!OzC3nJ3OP)f=mEueIFC?MT6N_Pqf2m{h>5F#ZY z-Q5fgf*{?EfOPjTd>f4SJvwhRseAB)0%)G3h8!(i&1328 z*2{FyVj=Wz??l!C0})8^2p*B;!w_!Ft6M=k46Rw0NDGN6ZY!92fPp?{|5h{2#;QkcWhN|R(sv%fCKFPC+kQM!jnZ57=0pv z4s9CUlX#C$yrf$mNNdTDjKwRJ%-Qg@VOP*Pmp?;Bz zKEdymj37XKkm?A&A#-=ADlaDWQL)I)wV3Ete(Zn5Cu4xmri8Pwg*qA<~jy6PtY?V4(fJ)}m2 zi@qK66MHKC0T0TXfoc5BOu7g(>r(tcii-?qAr5lEhByjOYLP^QmC5VHL+m@VD$jxW zw{CmiHLCL=N6114J4Xw>jn|8Y=7(ivyS91p&OO)hFGym9wYYd@=(-*$geC#o)jSnk z=a8>yKZwy~(v#doa;s#Rer{_SAB+&vdk4K$J6W|kIGP{>Q5WV~sdJV9!#C6E9RgQJY7T$ls#DKoToA(6K zVO29#Zu@6vr`w%OYenV@eCYS)T4GHL+KP=c z8de+@XKZ`Q;OwRm_9&{oQSFNY-}5i(umf-?B~(<{rw_x|w%eHUfld@jU9ADq_~rDz zA_H$whn+lerUnNf$nFN9(8If_CJoF!G|d>?Zv7?%1U%yLWM7LU<8GgwYnGT`4;MfR zJXC%AsingSf?Z2}Q0~iq%*uKk^3mgTB}3eUrRp1~a>}ObROP<^8uCL7Xo#`IN`U+o zr|C${6@-U(A=4B(?v;En{_I%*KmwN z<aY}|~F!ZQN$vMZrSw@V~mER?s%P)-Qq^L{Mj)im>%Z#%2{ z$t7##>V-E!EF&oM=#W5nfy3K?<|AgyTlCMtL(o&T+ABix0$1n4LJ(q_rT=m%o+TPD zgVIkPP*`G=RO2yEkmI+;K7B3+q1$=VfaVT0QFt`kz|C+~bUU0hnczh>nue-q2GQXP zWTYNPrcjWzTmZnYdyE$h>b*2nh`@Mv4ff8Dm!*`E8--`au{kOa2Y(hm zFZU?w+OiD5{5JQIGCgBx5E@r2Y_M3C?FtRU|P(q8i8`%8XAwK`a0fR~-3rJD%*!LsZP7_P{4Nhvs} z_l5xhVkw5-y-VTzp;)UNpHz1{Rg&lS3D|(fUcl1pg{g6mE2oSX5o#*&+vqO1b&57=v)h)#*&Uzahz4He! z^v)!6B@=h{Bhw`2#wV)2_#uj>%$T0 zd>J80D1!HpK#%SLng@($(~kFXn;S>E&jtW?skm(W59~Z@=tp_(t;t3eo0jlt=iOU? z1$0;|o{sUpdyAjIB7xk72UWQObfBS3T<#@&E_qTR;@YyiTdgLaAaXopW#_|}G+qtb zz3-;&E(G&NRK&FD5rHP zDv~E%-b49PHhus%CCzX0ZUg+qw$wD)2k^QS`!jhr6rO_@GLwc65G%x-!*Lc9z_pjQ*AoChUOr;)e(sPL$`9+!=$G{dPYXTP zv2tuEMnoDp@5DNe7gH-VV@6#vJ0Dvq6Y(Xpi$+-%h;vUgaH}FOR z|C)weTh=arRGc72+u*9-kt#m%1s4$DaVu|~fD-~+d@1%IJ$XQMVL1>p1+W(|4Q`vE zpufI$b%-DX)ge-Iw#c__v=gtW#U0TE>56bI;x9Cv*!vK2C6z-wXnSE|Q~+BghoWqB z+(ha*da0OL>tW~lWV%RU#IYypc~H?yFj7yl1bDKC5UHn3_l|A~|1SEoE{v5)bM=z#T6guYGz>zdB4 zhyPqRI375!q)@~2h+ilAC4c{J+2Bh-x#Oe5=D2yM?>P53&)Yls7|Z4fJ%Bel0_Zd! zKw{g>F}$ComBTOZ`S?EG9e#I$Fz1s?{lz{~uQT@sWr+@Ed(@Kn;)|&Rd2y&CHXkP)E?iIa8Vhe&2KnuvctG z!@|SV|8VDj{fJ%&S-olmFiUp2>vG*B=4{pNiL49(g}>h0`{3uoh4+wh;<2@v?yw1;BTBLS3rq z+aqk^lHxzvxB+xUH}v-$f6f?QZ?knEt{rf?)E1H(vr%6{aX6hj{H|Pmg7Q>om}go%NadOKKWi@v^2?{vf#w=-~q?9&1M%nAky}*UcRbk z2l30jW>^PI=pVA{BO2O!!8^(+qrhVjlk4j0(CBM7#q{*ZKg7kAVVxfF5$EJSiQXA# zT~WV7JpRi-`Yl`j>zgs!r-%A3eqG{$OeuXBK0HJNwAooFbuHdm_0PTU6oi88 zctVGkbNxrwIISiuVug}!eHFYQTX(K8;X5xGMw3t9srjA~lFP|c`gzIXvAeNelN7^Mw@!AzJ%^x}MK$o-z7v=5x1DnSY6?J8Yc0be2`+Ff;=je~a zfKvjp5w7~l`#*2zhi~`SSKceUg?Ez-3HZTMUq7IZ2Gn{HN3cr*a6)n&w_o(d7X`@s zngw3Bof1OZv>tzOazvLDY8m(c`#F09w}HxlJrn1TYuDV41o93vc(-0wT9}yB0h5!@ zx9qL;-}^&v-Xd=pUruT+@|aK*dX8mWzfipHMaOf^{az@@oLARCymwQgVa4yOnjey_ z&;5)JXTlFA-OZ%ab}S|wUjs|1;z(nUW1jFk3=JFY05I3>zFRK)3)p|%ufOc+Q^Ftk z5dH&vRBs=>!en`1#T^&>W)@eh-Uh`z;;x;+Eh}*R0au<^NiB{N-k?;Is6;bT!;sv^_nC|)qMX#L0cJNLXD0m<>=xceDmkj&+W@S) z6Iz=h1U+HFG& zx7jz3U~hUxW*sCH1+7tQ)l{CkFyfg71x8*+JMq}Qwg0p*gY=w^4-f5gVzD-g>{{Pe zf`4bat7hqQ!t`P84~K@i!6xUVIdx)PUjv|YFWU=C4DoReHYH;Uu*H*+tb0^lvme$u zaD8A}>NFK%1d5)^-)+v&#OS`=NFSs4B?SIay=VqsNl<5iiT}3*%?VMIQ#a-OAAt}^ z{e!C$-KSmcHx><=S;6Cpf(qBYg;eF^Zn-R~Mh1S@kUYaayNOG(9<4`rO`2iy0CP4o zK0nAymTVK~@;;0;o%e-hogOt)p5IF6^U>7g^5SzB#$ELr*m&i#%JDEqIkL|AQ6U76 z;AS%Twlp$`xg16;u9tuovRl!zBB?MMcHIN#1qrdiqN_&AvtfAYhJt!yF7)#_bL9ES z_Ld@qNuQ?{^{m?E>4VV_#TM2DQpu_u5ccAe_j+ZwWi7vo>6tqY7kx(nL2{>3tJlBC z{tptq!2spU^jc3v91v8At86_5LKX(sP5O12(4bLa>V*T}a0#r+irz;=6_7m~kCD+g zh;6Y>EywQ)tq`29Zz=DNev+YYkW@4gw9&Guvc?$8@J=RSZa8d$GH0fR?=ucd=ppH1 zma|H?!u=SliOh{rzM{1tutvV3K$*rH+lu0;d>qHax{K_Nk!B|0E_ zGT6UfKgv~^NRG@E$IOo%HV}M;+?vx#$98i;{+h_`3Jr9qk4L73_B91HK`R_WQ4P=Q z&7yccINH_N$f2(r7D-Z-kj(Rzt}(~Ro**&8&dC0Ni5z0SX__yJ>F z+^iix-s{YIZ-i&iqx%MsqVA=_#yR^YHGK@zERD{{Y)S_d)SJB{LjmQfQwC?qrMhl{ z7TW6R*3e=MlXKZ&o8)x>x@YeCttthAUJE`q6&LdS+gGISF@pCWp?N$eI929=P)CJb z8#pHVb`X-|SW;}cIM^ac0&J399FC4Gm3!ct5`9{2!RM^=HKJSgK7Nh0s(wM-=lYgv z!?LVpy75SxbJYo@^Y2y%d@K8emZpqrKrNCRBh6vK@``L-etjOR;htKej2keO*wZc< zSW12S8#E|IyiV6j%R_G_BfD)WYh%=T-{Dl5l~RXptlGl1md$M<$hfU@#Cou+eLSat zZkV%JlofwX#UL)oc>geL^cGd{gg^d&f`u;?^{)Uy7GdWP7({VYA|a|H zlJtI7fK_xtPOcYEGLa*sU?7=j0bO@yG&Wef9W^r1Wdt#pDjFZ$!?%{H@XX%G^n70K zmhk*ExQso@DYaZpFbZ@wvbUe6uQ4+mQ*^?pKvM@iFIBkmwrs~8CKffPeFrx|K?TU_ zNl@3Qv%y3lb4<@AwRej=TE@B5>lyJUOmgc^-3+EicZ<#j-3*f4&WcV>tEcPh^C}2y z$ZPT|gw+K-E`AVwI_9rLe|lqXi?qj8oJBXn(lK*GZQrWuR-bojX09#KoUI@kxV*Hg zQ(};5{yV6Kwsx=;)|f1c0{4uJ9B!N{#Rv~Jx8De>C^|eT8;1`=#Z?ZrGGva|Y}-Ti zY2bcn(3osi%<=M#hV+e_J00OTk*dQBon0&qDP!1nr~FFuYhp%)m^eDO(}s-gD#_3S znn6{SD4sBh8~G654+GO!UrTTJ)83+(_6sN8YMxEj(>!K%xgGuZ3&$G7fZwk=0ciP8 z7!8GO){-V53^n;Y&{vIg)mACiTk69F-_P5$7?;chMO(yP*OI?d<#+DK{fWa?NCtR3 zVF95{5JQ$5ak7AGjq^kudq9$2%bKGrtQ7;ZWRfF}G@wD#(J=SrP-I`d>Mp43o(*YP zf~HCB#?B#4xplb}-vQ_>bV}Ze&N)e2>MB-+%_KteoObvz?KH=@ZDhvy6HehG z-C{tT8?LqF(?yfpLJY=6H(J~PB38>38MkQzLOW!mh0D-d>1;ppeNts!jEZc@nZd-Y zn=@JqMs3N!{CD*)iC8Om(62&g>wRG+yr$b7T+ef1#B=%Ts)KTV6&4ur#zVVVZ_O&z zz}F6g4>csE!)3lfgpqxwww=f&2L%Ze$A}x01euIO^k2-~*x>(ofs?U1lUFh6+nMal zab(k6+WCMa`o4(7bdtmKHJ03|muRrD!5}*)_UWByCi~O3yqQkNbGt57qxNVt-uf@< zlqv8USQ7VU<*B~?jETs!9y`#+%vUk8f75)XKG=4UwOw#kr2xD~#UV&+z98F~yVF zP_Bc}d2ah>*b7;FttFuW9n@Mb_^CN;c^x$P{3|V?HhJL-`*|>_3A}zhhk9=rmUvF$@T7 z0b}-uDg9tXqz~RLDIZ&c$Z~!Mg>D0G9NQ;M=vRRSxn^g4_JD9(pKgu)!G|$h(ldxKv?B^1!BuJV*qg?job&~@zNRwN zx@rf7vJ#VTL8MK|cg5=UeXSM)RlnSrm4*F` zhG7IBSOUQ(+X}>p@{ny8`#sR0+He>e^eAD}hwtb)^ZXW^OaeZ1p1NIeH=Q)>Z@2gN zJ|ltNi~PW=(Vsb2p#xt5=jvg}Mqfem%Ff5qV(sX(*M(&tM!6ZmQ;&G=;#prWjN)$0 zlKgHRoH}M&=X17guw;c3)-Tj3DXogBIo+{XP5)S2Pmy-OKAWA{mx7Qg%Y+G` zz>%;{#nAjyG6gSY7RJde4W~|>z;e-MH8O$s8PqOp>K2IPoKTAjpW|}Q;h^x{nm^ug zUKbejVL4N+)b5dR9ndYR!mz2poU_w}<4}o^$xd%n;l3W#%?xPx;Jc^hSY#g98Iz?p_haUFyHANn9wFI&YZlf! zo}e`>dwL3%1DbW7^|O!qjqvFxZP~O%F^^CY1JH(ru+~?4NqeXNhUR|_fc^ZV$R_&x zx{(C;u6cJL@R@kC0t94=tWWV8-ihy`it;d;^nez9)_Wl`?Azda!8?SSL9R6s6u`y{M>HOa z(>sPd7{>F>_;iynN?(=xt5=2_qZJDwbEO16=-6@aKOhfa8byj|zGa){$g^7@agL2Di(3@aq$5nK5);p&6n4}8I99sX{pl@r z_)!S&0c-ir_G^i|o2$cMZ0XhrGsTuJ@MO4D(}!#7K~IOIX`Dc$6?uy*X9}C|9`$k& zqK{M6^R3graaC5<3j-;<{AQZhc<&;XTzF_*(G0B)!E!9-gBHa449_GW@`27$;?md0 z894jvt?UJYp$I$fB?X8V1EEUUxnR8w+3IKjhBS7MMKuaLqs}L_9i>$UoI?ceRoywvr3cZ=XVjHrxsv29YT0MkwgL~ zIPt!yVoJ{8mK$04Q~p&`d1+T`=JicBC9UyflveR`kMCOoNH8>`jQWjDiFa5P5YLmv zA98FbM5k|Z)T=~2&#PsxbvSHo2j~%*4>C<`Z!{#|Rv$4?@6rW&Fw8aUU-A;&*Ywpc z(vVf6xkb2RJIY$WPyJjBK3+rAi0`fh`tIFKK~z+FQ$FavWlrpMQ!Q-WO%PT&dF!0h zTA#}|v}owo4RPqD4NW}JGpiI-U%O+BRa;Iu=k1B&?vDF?Q>10fr^A^_oLcsd6kd%5 zrB+3b6SFN3)Cl&CcT#`n30O2nqe)2}wLMg;FprP0yv@9ryDl;aY7Me?wLFs|+vN9Z zY`BYh&IWx6M5Mk^;%#NWbAY_KM()!-w^RMlWn2IcF>KK^4OopEldLJ5chjS}K7LVS zK}<+|V@iS4U{hoeM%27RHM@(7~V{Ixl~<)6E_gnZTK2!8OQvB_0CE_*U`DRw?}S1S@mcjjYtHh!4w*go^H(3Tpx{RpE1 z*TRH5HMbu9FxdZD*Sz!b197k|kRddyYGN~Q79aN&=%F-n(O?l#<$_Qw=_(n=iw0;6mSiex)3z)1 z?3i`2mI-bBJ*Hoc+NfxMZGm~{)$B;DiJNUqhnYP_K?=GyxwUqO2oY?dHI`$=szj+( zK%Xa}vcaLHZdQIGaeuOJ1QlYZxc8N%$@xHO9)doBRnZ)V;Sz=Rs%zHC{XyPy93qYQ zu&n$$3@7OrD+oP@&om4p&qV*ga6tmmOI<0{xEp^B&}k6+zt8Ql_#g1#O8*G;3NA*f z%#V{=8h-z-K8hOA(09TX--vJbLgtwxPg*6Yu2b62htU30IZKLcnQF?cnW_-uX#tw6 zI29?fs1lCGmjJ_I$w0SN-0!@!O*4YqSXRF5lQ;pgDX1auIly35=*6|unls$(lZT7r zu*Y$q2Y?6Dfau^)hG39M*R#Owikh68u#;}vLgM@aec`%9D`&9ADT# zTzd1?QAi>0%l-#D5UX2<2@SJv$ZwBcV4ym~u4PET^C0$&o$0F7t6RPV*CZmxM}~t^ zCvK&{>WF1wXD=O=Kly517?27Vtq`tPpULl6Y)U$*uuoF3rubZ^fAgq9W->=|FABpg z65_#7BXP*|xTTQBxu!+ZKhzYLYDX9p)I^B&(IAu37z{tCU8}RZua_*75yiG+yS&4E zycaz)|J0UgHJzi0$(ArP7~h+A>Ly@nGEf`-39Y@sKa5m+KzZBF z6RiO?6d~B}A3HL@Inl$1ekWhC(1Gk#zaI?fub}E#AHPvLeM<+LLo_wCNibKS{EAU2 zO+tyB=mK!p`!!_J1EG?~r09OQMBceDTU;N#^FY=6UZ!I;bMO1XVx!LHw%lY>tc{z( z7;$JavQ1!u!;=St!t_lLO1QYFoa@)?{s=k~o9S9DeCzC&PLomDP#47NGIi5TBN8Q0 z^$q)&lY!T!g+eKZVYNu|0pcN8lN@>ILPhNNfRM%MnVbFkHWD5?rL=J- zX?UX{nhP23vpjN@r5>M*Qj|A|9w04^7FbgntnQYy3%U43kmr5GVTm^BX9W8i#Hz-c zboklChOp=b!Xq;dc6)J%lO7MLK#w3F2bdbtQQzF072+l9n?`#Yup8k=c)Pj|&hH6)Bq)pjL z)>39}V(4rR_(Dpunk<;{nVm#j0S?|t_2t}>oX4Y!!f6NC{!QB%5F#; z^ZRP4?G;rcC?AwqDaq7liCsGF>7k)ONy)zlQiCI}g3N8AbGG$CI=O&OxGJ&->W_e$ z)89tHgL^+h%#6=0JF^ULrqo>J-_{2mTA!2i^ zityIO$CY2ylLdMNq*sgs0@$*{J{N9lNZO+|fk8OO_rN8A zRUY6#H9Tl5`3ngG9sJu8t^JGi>;Px*dv*#xIj)%_9v!K>8RqmPlRoElN6UPO#D3Z5 zewGx6fS2oBHF6qy$4p}Sv8fiP*)L(Ra!%Bku?nk-tfRdet4Nz37~@qn|4XWGuriVwB6Jmn=ieaqG|w z`b0vgWM=okE1887)y=!bbk;ZZ`+5!`JeuS}yrY`uw@Pug&J^V8N*2B@~-#VV<>ae~Ek`w?M zx?Fpy5vbRN@b*rNEEhs5FE*1N-<#^@aa1QHx*s1v-PPu$G8G=3I2>1`x9=3*++JHFrb>gf zyq|VZm5al%t-=pC%aHhR5cIu5LLvryU#)3IL}JzVK`*&TJ9-w~T8Z$^`!|nMcgw9O zh^`cHji<+Q;I@m@o}9$JdCPhGiAM38Ma~|=SJ2!s+f zf}$$p9yzQ~{8}b?zq)56GWV1nsu}PvP?68g*7$=H+PN3TJR%lEj)a)OWxX>OFJ3RRIr0&C;8Qi*F zyiu}>i}4r{bsWarPC$eW7)FfwyQ1bF+-CxYs|;wE&tKDgarAc|j$!>zZ0NO@)DHto zuh_`G^B%P|7(V43K?`SQb`0LukciBG*95`45Ci8#2NG#wPUQ@Ke47IPhJ7l2n*{AK zfgT=GPDd7)=rEdpHu_Xvt0Dwf1^+zwBL_(+nB!iby1V54b;o3GO&^ibkn{?~k)6+D z#7w=1^pzB*AVjiy;)K2Q#az_Msq7i6wYBwVu@k$1jf-vzAXeS2+TE<6a0RoO8tVM| zk+nBmmfuwTvKh^FBt8iW3Z`6yeUxh{V#&blrmW@7@6jYRIcK*hI2+O%gTojB>7pb8S_j!Wo5gb_8XivA;31-c5fvkoCsI>skulg8hc4@l?`kNU z5Ius;j-pE`e0MbZGKcz!Ng9G07XtajGPANpw6bpE1dt;I8BI=&+;SfbsxFp_tYIqM z{a~g^My@~JtI9>1WAU}Uw11HLe6?o{NIT}>&;3aa)zji1JXrf@JlGmSd?{eFd|_08 zjvxn&J||bn;~Ysip;8(M!8fSP@Q8CUQ#sIr0sQ7=b23^}v1+4%n(EVl$O>8BzGsZ& zxe^hCZ|oi&hVbz8()D@rUq|CHxzg=itn_663b*FqH8 zwZ(d*qz6gm9uB#xh)jVyo4Xjy{RQ0;pJwJLysVBhz!&95Z!Ex9{9&ZI9_K7qezguXS23;^qyA|| z0$`@0AiF=QgnFQL6^XLd|BtLNP>%M>@PAU~;y=UGDUz7j6P%r@O*6BP=WJa8b4;xT zf`?c&m;uoo54FNh9eD2DPmy_%hn|=!cU(hV-9J5@Hr{!BrE)bpXLhH0XVUTOBNQXyXjVv&|0!xS zB9x7N=W8ZVbkG$5ZN`uq=lc8lL<~JDXsXP8MOc#xT*G7 z^4LknNRLmwrC5nlp^>X#|gFX65jmqKzXg; z59~8>4yBNxrJNf5FnmP%LK5xz2IcEVwMavQnS(=9ejZ?~d|Xi=&AOayu8sdvD2aAc z&ChAh*4Fk`e%+z=mn30G2yU(Ij8Lvl1;s!H_gvD&3BH+G?uajp@zwUOF_U7F|28-N zg%q!<*PWAXThp_pcTF=1AnPizwgJqDi6AdD=mfuepDrJz#^R&noHJkm6Oz2p2Z>*K zj=xEJUpm9DHLl#{p+_q~t?mCV+xV}AZ-BZKb!&oLaN!{iaWJ8RGRI6Q)0t>5n!(d* zeENjvBeW?H_bhU*G6=d?)NRcapzz!q2$!VdsWAkbiGa@&3C>Sx`nN;^B!&*E9tJWL zj@u>C0Ty2QCi9=phbfYYy*H9tt~HK7%jFg36z*(K!+0Fe;+YDEwnyXLTouA z#Hs|%044+=IMO*e>sjD>AAl@H0HXDjPid`+On8b;aDJw@(7H;VVElb9P@04M&=ZK z!XoZ(5e>q)Vw%3SGLviDqjTA%%}SeIb#g+}@9?}NuVDc#{gF+5iUe-}d8*9XOZ~>P zMq+=0@HJYMs6?kp>hn1o?N1O>V<4Q`rNW6<e4SC!mU%7%7j_udF-~(@ z(9L-tuGZ&Ct;}`rjpy|`ARzY$cAkKQbz>5ZFQaL!QWt!#kLp75#?#6DXnhPHu=v`A z>%xkntvA|>tS5$nl3d{FO|8I_l&_bmel{s-oGVN%FDG(b?rH6eVxm#rt^yJ9@`2%c z)z9y&u938@I;eWGoRbcXp%{H@h9!XE=WF3JPZf^1D(mDcC)qR-aD0vRad3_wW;hg~ zK?_=6jT>vxeZ1tv;lAQOiDn>$;9dDbHbCk0A4D@`UVpF%M!ku9$IK_skUu^kNvzZo zh(Ul)3FmBqlpDfy@fzd1?=UdDOx81KT|>#P8AQV~i`h6r z4C=E(tE&?oWi(CIMu8c#s~hn2+iGQ}heb}*xYz=UnEXPDE=(iPhO(=z0wX#Or#*=u~U4x!aF7wWD+v5A*Q8OMSCrg)~UK0aJHcVg+@UDJLFKN zw)c%y?b|-LJ^A`M09xQ|)p}mD)tHl@Q@W#sY+Zv^ipMNUf4i}Em8R07vBm*>l}O{h za10UrBeRs~M`7N7#QpwTVO}y_;tMg9N<3yz-m~TfBdPInaP6YLFTXb4&R`?)L5k@E zUlD+hWfUP966efLV{^`3(g5gWUIt<%h;XnyxL^7_>M_bJpTunIYl(C6$gf^6$yAm; z+ANYPjlZtG&D$qOldJkTi1)HoCA}UPd3-t-WS`q zqML%rvet1-JSh?U11W|CDkS)^HTV7+68&#m1Jo=|^!FQ#ZoY{1rq=0&Au0k#bUNh?QXGH@=l;*%(O^)2KJ`DsT`p z0X`~$yPj|1HKih?f3&b^X&ohWZ>33-9T7GpS|b6UDmP@RrK3jlm?bg@1-x0^N#LU@ z9b4yl@!g9Set?brY-Qd!QgP&yx`GU`xpZ$ zV@&xx;hT;*#L4QuEBdCVcp2}C`hheN_Z^1KpPi@wE8+ToCS~cJe?*Ld*0KLx(#fs% zEb#M^*6*6mr*8oUWs7{XT~SBqF+n--NF&f|ghp(7=WT&CD_^+~YT2MK?7&f}W}oea zaqPw?OeRMv_{QdD6Jq2rRRX1(*>Nc)J-sdRL{Cr8Y^p*7eD`{ZX}6S<)9S{zeqog! zqbg+~xFw~4*9N)5!qY9G=eJOCgQFWZl$&OFdGhASZmzZSGK~`k$Yp?UPAsu4Wmj=5L)p#nC=Kl%X|Gg^!win4`Yd60%BQk+1q2>rb;fiys`@cpoPJ)pJnMSFDO&x(i!2>>r_ z`^O6rmP_TOrEA5G+*;LDz3k?FHk;VX;<+z^ zkAc4b&kqT&T2|y(f5!3eLN4RBfbCd-*J&gCPV#n+3X}RAmmlmD?Q%5k-*fWMkN)pC z$3TN%i`I7RC9-kDEI$eX%^~3-LHz%aQ9ua0FC$S(?zHa2$Y4-mi+xvfD}YKUav0nY z#ZA)?y@B#6YoGR`*Ii^8jYfa)-QXLADI6=3s;~85?W@Wh!?@e^omt&KFFK&ReB-9< z*zEbz)8lEnwYl-dcG~M2+K=NKGICG2(=rDM8r1PaWq{tTCde(z2Xr}@a|Q2a?MQxp z%gc)a$rR#$7tYzba`@(hoodeagjKvFd6+O7w2Y{Y2u0-Dza4igKz7c#{28vI$8iHK zg6nR$2sce1UM)Q|XaY*W!0!d*(0R_-Q0594WB=uih%^4>z16H4>(q03nv*8!wd=Qb z4c5i#&+Y6yUZJ684nPN2`&LLf)WQRR%&yuYP{ik}(AaEiy6*YvAOi*lhO*Nwbo46b z1D7Gm0^KSxgOYpq?kOgV`KcCF3TyOrBODxb&eZaCdC!vPF@6)_pWe*2p~RD~^{nW; z+UCv(avwdo3qL4Cbu6wkNOiM zoAM2^j2+OH%A=(VaC}%`prTF?zHQ08sKy>&+|<#+dRK3iqU(Gjzs|`~QgzuZ$lk-y#OA0@}@IBR<>tJmpDh9Iiu^odB1EZ%)-_s{DKpa8|p<|fk_=$^cUE~p4O;1vM ze0+K;p>WftNmUBRR$xwPK>_Gm2=T0!mls*p=iFMX1OZp&AZP@IV799b0oYzdn)vfOBG~FLz)hhl3ItPjjHPx;knc`)}m7$M+4Gli8Tz?_70l{YYCnEf2 zH@jv0j!sP%`Tinuo$gIbcS2SbZoPnb0N!JIHzSkKk#P|w$k8`VyMxW{X4vRR)E5;M zJwMqsz64ju3!AqU5!@jj*B?hC-I`DPavk|qWFrZv=e2GD4likJs*e z9k9-!@r6}jdxYzc0@La3?d>nYTm51uW%qDxD=A=?_$iXgG%nHEDMMqyeMsT}#DaHR zvGVp$DL6`X7q&QmM+2o{qyUn7T*h%76UDiw=J~vS>4YxTA5QAV6^FmvnR?Mg&(tx)*F;zI+8b3ZAVHQ58MZ zeb0_eOV>C53oVt*DDw*o->uf~&|Hh2Ihmcko6|n?^zd!cRHKT%e{tOP&YOQEGA_Zl zO@^GvHjF)2uq>cbk(Q2&m?m21^;80`197$m>+ISBvS1x?b*EBNL?3IZPDXv6dF}4U zEi~AP{)DcbBl7$SH!p9{e75chE{B*TT zFR+jl?yUK5j~G&9O{gfLL2Yjx#n)%oU_xEiM@4}rFFp^Gl=Jp(RDGZw&3Nmxxm+d1 zPl~-D^RBF9dcL&JAR`w_Ck%n?9v-HXa9S9hdAtCCRPNJo<6rUf`wIg8KY{cxVsImi z9sp8+yF3I4p2Fg|tihC&kWPHu4%w*XwBACy#f=dPI@W4)R`i{p>}A)ESNT{Uc#wN7 zLcjWjH$X3XUWPX|la=y-IH-u5QA1DMW&4(WCdvCp{CLHfi3$xWZPirp1Gp)(&epZ2 z8LQScF29iqo%gG0@P!kV9=VVUc6YBy8m$ceE*heCP)o8PoAp zRz_~ss}dt-P0aYzB2m4Z=sy#|6=!2tS3?F-nudzW}QKWtnjFrV^VIL!tEor z*0#iy<4PWj!S9Jy1-Sq>8S|s*{;Q4g`wI<*XfFV1bvn*FvT(?#pKenfTzh<2N(w*cYYJyZ)lfkb40ath=~1j%clU zzPTk#&vWYytRU0QR5W+ARd*WoRX@s+ z|L@CGLxy)PDgp$cl1q zbbIqw0jXmhSW)wl$8H1H`01j6j6i*u4Doi#_EVyRVjT$0>A@bYEe+y8*#KftR8Y}! za$+Zt;}LbN(OIXqVL7xxZ zTmdEEcK6esL*uI(`+Ew)eM31KPfAXYj}?KL4Oxa6@?-s3Ri5W=ZthERYX?3)KIY?b zw#sk7l5%VBbf^n->*WO955Qv0jSJtI(4Qy)DXLZcA6>(<&7izVf*MmY)#L ze08f{A?TMjam41-O7UjR_%zKocV3SBp%hTKz1wY@bJL2dXF>Z zm%8!dE^F;_Icf#+KxQCWIZftH4-)ixxE%R?tzzG3W*sT+e8|^r0dpS=LMG+qo`fu9 z1%a;3ZD5dVAL9IMCBJ4f#UGpW?Pv`zA)uwQq$4O$<5)^Y!o$PUPpq1$f9vfRwBA5A zet@_uW&rSA2CfAByrUx1KvyT@y-H65Z$V52R^QdcmP(i+re7gk_&CI z!q)Wj;|{z7in;FcH~V?OB#4mV6gHU(ljja}Ig6>#zQ@}YCmb#wrmyPeY^V1$3# zcx5SiyoMY|p@`xO1I(%h!(hfASTK46a8&s9$y!(Tx<+tsi;fTKodjTPhqPf^X!0s& zwrM1%$_fN}K-j(1qwMjX$DAmNS?3ipvXSyTA)2F2NTr=9dYu*eV8KkFKNh)+>+Yu; zLlHO4a3}=K1Zk=jl|Ha70uzGPB@u{Wv-Ka_Rv9QlhWR9^h+Q*jnnvO^|BLOX7K1z+ z60^XN?ExlMm=#Z*>t05d-dQ-o?eDFxBxT)LNfe>`uHqK z5}6i8?2o_c<@1hCLHVm`lGCa@g(1};@F)jcL1K6wJzE_+Fe2Q1C|fO7NWph?L38vi zdCvdE)?0=}*>+LGw~EprEg+yET}n%efHX+Qz<_jvC>nH&(Jjt z@1@@Ncf8MgeE#6?%y?brxz}E6?X{J9c1ejCm2&6;@j-iMvs>xagXD%HLx)s}Pe#gY zVQl)pB5)i1u@knEhCjy}u9*A>>Ll)0FM20WmFO53B(@MKr11DMipSN>UF!bc95>of zXy>%&xC!3&s|Y?oasou3+bbtZRRtB=51q4IKt#p)FWXwOvT%(!>3Zrsg)( z1$d7j?;WUVA?FBI>oC_!BASget-7f@wv?57;`ZmKd$}#Qauo1S*R|Hnv5z6C9U+rn zyA2YAZJE&iMJNQorTFSN-Rqp3eN#yc1{CW++7sy*Xu4Rcz(>C_1Ag~=q(|_eD565= zgOIv>K`)yr*{i-{?!xz(gz6O;{0~n;9$`qH`$0SMKo<$KtS2I|{N+(`r3ikOA`D!b zSJEqN`hr`+u*}=vFY>ec$1jOceHj@M_z|%u8OZ`Gij9eH`Q1j?Z4p`K9_P86gEU+U z?yrc__`C{mJ+KZlyy-UT$I`az-}t_-rX;FbI*Qq^SWk#CP9PGY&b~@de;^e z9TS6_(BOTWv?6eJL&qzq4CKgxE=1j{H1J7uS{jtYCxmJr&HM)!9`~@^<6bF`)yc77 z1|O^`*3I7Hz|qV)mN@husg=%!F$vEn-%;N9rWB@^BI?}3nhS+4_^)m}R7SHFE&ZT% zJ41SE5T$q1(_6eS>ANg)l8cB>-7b$Z&&R&pdr=JCmgrU8xT=1iWTg&G?u)Az_!Sg> zu8Bu_dgW*(mTkV+&6ucLZ)I9A1mr3A!U^=_3}3Uc?JZFTbbR~^=a!@XU>-gU$Y5iG zEt?=^m?Ka;w%0Il40FUY)yK* z^t{dK^{9K#Q}SP4Jxt;hPjV+b|M!ynQH!?jG7$C8iv6!xNt_epr(vB0qKP5b)5`PH z$JoJ=hR?*~DfL<|apxM>_}N3}NI6ua6Ub6zRaAsMdLBN{R+7Vjut$y)p&p?JRy@0* zD$;&6@NDxdV)`LIQ^@a{?xXPg{nIQ|jc`Awuw$aViv1!Wa z7ik;j0uVoR0~;t5Z9Em&SXi0-d7cGmcsJ*{XALD}U|;CH)RiALk0FT11XZz>n=x%o zw>R};NVk}V;XWpJ+mU5)(z1(BJ1<^xufSsq(~q}kzl0DU4XH%T$jb8Xtjn&w_;T+y z-Gma^6c;T?0bm45$YVy~@!<7oXp09J+|CZ(keC#cLO%5r06H8@PQ2c*|=QngU z2mXBGu%iU{Qy5>EJnQD`j-_{aOD_8>R5e@OUHhf{;aD1O-``@#=l*lPne1?cmWp(# zX33XHbSEYRO9p=_*Bj|d*t`s)&BDzKt-KqF(zj+p&0}I6hnftx`>;UAZXF&SVX=|_ z#7f8~tW8#7vj6Yne^6l}E3e2jO22>qZd0BHG)=Q#pSqf6p;z9F=PhkFSpR>QaqEG+WipfYw_&W+2ftI977%1P%5pI2Ch106^* zle5M9NXk^}90ms(!@&=(e!uNMbncU5Le8Lp%=-tQccLw8+)g3_`WF@u&v)k=BM5i; z;2+&PmaL~aMsj3*$Ai4PuvM1G7!emH6)Yf0D6Q3c`H&s~7a?jFCjJG7X3;-^SJd2) z+i1O+4aq*R7<)1HyY9-2`$3oOe?)ISYZdo|@d0sOG)bt<_uwcNr&>&Vdm0K}cMeV$ z{g?Vj_GUI@=Kj#Je5Q2x<)y1B#Lv&qWcJYqwWy)gHIfFx85#kP2MHrkh1-(n_ zeuXxguUB+wV$gc0tS`B-CAV=NBp~^HUUQq}lC<>Nxiy>rT27q%JD8B%bmN}L9rb#! z^U(Ul$YYW*V^9#}OsU;L)CllA2*Zx7gJ4RBGck$rGy-359d& zH<>)lbZdym5EJ7(nX9dE{LZtw)!MVtx6P$FMgs_*7ey=JQdF#Udz(+c%^BdZlt;BdCNUs!*CV?`sG zh$Ag0CJ>kt$XNnp9y z(Hlvm6BfH>D2wP!%yB}Q1ktasyhQFi$D~oZ(Clv;fPmG>?`pZ57M^Crzm!h{k$G_4 znt}Ernwf{^Tbschb{w-u!onvTA`0GupJ21|A~J3rRJDGf63;Drzv@h#+ee%~eQe|j zn%!Btsp|*e=u6iClS>rB7Lm^Z%)G)@^u0SZ9>pU1qO#D!9vZiGg58lyV~Tq=z)=vIS*!Sbzn^RK*hni& z5|e`4nN3Pk(xPkJrtMG=EF!*7ND#B=vgCDY1ofUeUHKWnyrPZ;^96`ZHQ$Go`{XLn zi6#tLfa_4W;I(`UX9p_;;!znXe<=jdm@MZir3x0!SG??x-f{E+%#6`EGf0=ir{yin zQV+AU<6cU3R%?==Z^azLA$8dmN z@3QKi@8lDA8&SzB&Tmcl~7!8zqAsC-UI})IsZ*txr8&9V=X~2ida>8i8b1=)HTS)X~b= zKyQ|Pwl`%NXax&P;4N$Kr!;75i3q@#XGK1QJm47jJ$-a!x)IdkrpE5MQ6lf;wHXPc&xPG$RMfgC247JRh!YZM4LREI{OXug^5Ecr-)79tXU0EtWA&(2R^{rum_R zg>Z>D(W;~rt)bv=-zLax7ZL;^4WA3EP6)MmO6nOmkkRz=s`)^t=6oJSHCdJaDH*2%KE`1mg8(F(&~rG2QE0u*Y;>9>Y|4EW+DLO2Hf@kUp08Zo zQ1JEPq?H)3GDy99N91I0s!e&xyLnE~&W=6S#nTNZ1HAAKNp%t}N^$d-AZd*KO>f}h z|1Z5U?xNdVV@O6~MuMuUJg^8dU4h$?0tBi<$T4Y=!oBAzYyvB}jpg*JMo2de1w#k4 ztLy$R>16s)mLpP5KHV0a>KFRKx$=zXs>@;3>P=tBq}DguWvHLVcC>i4G$Q{J1-JlF zU<}-2&wX#kzq-K%bT!63vwtZV9u2%1uNjc!(0iHi2%m>z_G%9fK{!swQE}uU^}BHT z>v*geqe-E6Q9rln=>E3&9*<}09#TWbt%G^3r`nk`^jK^Lz&IJO4*YaE-4|?T+(03 z$co6&U}s zCw!uLy{muzjjZTU1h`LvVx%yQZ#$g1b z&hO4IfA~nuoSe1>7`17)r2taX_m##~H~X>mUb!cBO#h(&e#DW!Xk4twxT4mf2X0(Z zZ)vVisg&bbG_;JmMLeAIj|4p6t%DZRWnrIJzN(yp0upGvQ2wLB0yG&RM5E=d*Bh^2 zHL{p!Uf#_4VV(k91^JWU?)B!wBDuTU`UowtmspVYr7wMl;%1MIawFG--S!*7h0Xz; zam;Fo*JtTH;KdeMhWyvo%{>`cUr@KXGR+@;e9@Z0Ym}Z%FiP*s$^K?T#?1EerOd3e zKQ>tvs4g2bkpLyP;9j+TIZ>!KwR(b(!vca1pJSt&bUlP5&fpWVA*&B?XZ2?N z1rQZKXa%UGVcv{UXpwwHq= z4erq+Ta!{GKWcFlQLN))8Y_!&ZEpe1C{frE*OQ(H_kt6QcaX0_5%p}?Hnd%PA;30P ze^AF#9II%4p}j)Z&p`Z9r_^Z!ohe<^p0Dd-jL$>%%cJNe5?1v*E$MH0zb^Pjvr-p1 zxCl_FXut;Z5D1^41xy8YL@9s#0%Z&i*7Jftjh-VFX-1K5Cl*~HBmoM;B4Q!v2y51l zZz_#Mox8-hhL=o+%iQrBz*dM4x7Q>!Us~ICeev(k6F9X}hf-b-rC|(j$1u@yg)F-7 zQJoJ&Nq3n~ys~FNQi&SjNlB5cge%ySf?S#H(cM?dUEh&&eHMh#fYqyV>h)LbEiBMH zwps`3X4Kq4IO5)`~xCu{AIn-R3F;CeI~~>A%Hpv@KGks9 zaV5q7Y&_dsE!tDHM8Yz$ym3BM`D498G`RIlYP80(dgkU`IQv@<2M!^XxRudByL37g6{y{s=Roe{BD5%hPg`j+uGQ&pqbQK zeJrw}pT#`LA+V*GQD(`<4zilAOd9fG;yCZJV5?1~q1Fw8#_p6%_fI|11bZUuiO z6#`%DO1-}w7Q1_L9IU7b&=+an>wOuQa={?3S~x=3muZ z%K9xm*H|(i^14>{q4H965`zEviovz*PeI*Ut2og_C@0qYI2NZu=TwhINPQOJ;{>+e z)C!D$t9ktIi$d}Lt>#dNrr*_k^BW+kMWxmpB;Ye$>2fV-Y+;75sHP#c4LpvZYNykv zgCesHmZ=;Wu)AbOboO;z|$XeYO7(F1BH*09WLH$;25-Z>DPT z-IRHGIKEY0><$^1Ig_WDo`pvx^l7UlL5&(x+m1~j`S9T^8A^`IRL6un0?H0@HZHeg z*Dk0CTueGD%JgPrid@&&?=)iJLC?BO{wN{YPBjaLGBWro96Jm+#X@e%CWxc zJsuu+N8%%lbi@D*vSfUxF8aokNRktEH_;^E?he)u6P@`kb#`qSH+bhKO{t0$}-V2*O- zMBMj}oUIC1M@IHOBWGUCReX4G7jDOmvyH{6xc=R>PfDBW)Rmu@C`JzbUhUU!mT%*=8pd@3(rl$k}jA>N&rHx;>X&QZie`4Eb*PBU3 zznQMKrs5ew`bsa+)I5b8Ok`}q4ut0NvsZq)7*tXYwY$ZULp@6yXbR-WL?%p z_kQ~Lu@Hz`nXr*rXu_?1d$Ybaixj;IJJWXNx*0N5zD#C}Fx^N4Ix+$Rqdgd4QIWu) zNjI@tD2mGrzYrUV)~(sL3LC#3DkYe?_eT%zar#4_I{rWOX&cde?!)GSm*##@Mm%^{ zshHQB;Fiy4`cAs-M#ZDv5NN#FKZa7y3>uMS>`g1HFyOv$Fs<=Jh zcz20(B^8mX5TKuDsd`mRv-%guJ*5LU*z1(en_roL#FfaPq}vCLd0rGF%)|5Ptv}GF z<`NKDeAPXmWo8cEFRY;CjEUbRS0u^^dG_J9=~Re${N!?Pd)XgoCnZ(ZesOB9j$2Qc zjr~Z_wVu>~RUnW&W&`)nU~G$;k526^Wy(S2m`?iwUmg~L*`DZ1bLT1Z=U8_bvO9KM zlEq#aA2d9})(Wh=)Hqqr=*q9>B|^!Wmj=Dx9Hg=r&}e!8?dcg_>&*~`9o%<{C#+f0 z&tE@Fc}6}3V<J+)maA& zzGuBPv?MTW{uz0RnT7+GueDl`P!^f&cMl0NU{LZ5X5>8T!>E579KGTCFoZd=>Y(P+ z8EgOxvN2EaGq9TMP5(ONb-V`SuP{i2KlB5S*EPmmFSx(v86U!LdT|SmZon&U*coJZ zN)sAbyZlF9;=ku*GBuW!S$$$#{$7a&qmGb$aJvKtR z7TcQaNr9L*q;?qS&?_%`xmti)e>RNL`4s&XJ=b5}1J1qA|Bu9<9|cJKDS&|rqW9K( zxJ%U{lY;m$p(5>cV|+?Zym|arptBHD{=#xn6avjS@1g{yfsTT;l*YS=gnIVo9)@5H zN~`$yy^k6@V|tMl!~!?k7yE27O>vCfBTqu?x`1l{r2rZ4T!TxKdmt5Mg7P1p zESjziPzqN#k5&kWIHw}7utzEgjNL5v%XDMsSXST}WVjzuC1|4_G{mF!?O#WE98b}# zPCg4sfL|B9FZ=$XB3tJqzGhn_TZ^doqI+tP=d%oCuGQL{qWn!}W~T5>0}(E@;I}TG zcyYql(%67c$=+H=UR=fP@@Rv8~yD8E95+s^yY(OK)_N+fZHj+NEOTK+w zHfATmz`M>YW&@R1Wj_7mjlo>)r?3Un-cBKLXTYNEbFVNms3>jkELImwr`Yfci+4uI zPYOXnv8&kXUdNZy?`V-Kgs;9B-~v}-MIMY)ig9+iLRpkLdx;ou_=7k)S(h6=hH}-W zBmvC6tL3G`PIAr1HGJK(NzM2`A&+x3DiJFVB)k3`*9T@%6hmk>H>;BYphMS!K>pC{NtqKA;RT+^rPA1CFkg*AnmTTa^bxs z{X`Pj#<()+*zra`Zha0Wr1rSB)3{r;0lt~Hn=a^bhy!Ct-YI_o+U|*xup(?|OIOjP zd)(eoUY?*!BF;)z4FZau*zGA%>xVU`dZ)cK_3X_BHmpOdZ3FB1%U4!j@||qpj_V7! z)i!qVz~Wbr%6dagA0Mz)*7Wvhw&h+t`xFQh2Pdz$9FjAJj=w8I#tE*;ySlyxX_-aJ znv~FK@}0_(N^bjN6jFZF4-D(HY3($tF$Vtstk;x>@lGoE)X&)cjqq)My_Ay*M3aGamsd;i6fv19JwSYE9wefcOAD)`+S6!W=6~~@qhNL-~K#z zCF<&I5!E1J~?h=W;->9CL~m!4*o+7gXXVboNQAL3#L+Pr;16OZ76RT8V@JbJhh z%8-^L-Me3Vfb{P#+7Ci_3WGQ!=E&^rwm1=?SX;pu__;qoN0y{sEpQc`3& z+eRCOFR>vf{$wg!`U1C%x6QWnS67eT=RTq;d{enQVZU0N&h%vvsGVk^3x*iuIx)#hpP5TH0} z2v9@b9)e-yXGI@mZLIG#r6Ld*x>u7I`?%#*yyqkB5T;F3z-K9biHRhO>ha1?{7HOX zb&70we+r-r(Q-OR;C(;nj&TydJ$0#GWk+P8c{zc4uADw^1t--`BIeq!a8pija!G!V zp`TRBN-0|H)d@)O@e-)zJ7DcG8)OvvDLwGv$bw)FdPkYW4ZCc<0H~ctQTkX1ZXrMD zoTI;L#A=t1>v*NjzE}#=tmJ9)G682pQ2&xe_vBq_2juiOaMN~?llY&4DCH*9w@#MU7 zMywW#$ZDfzw!8UPEB20#TwbT6j@M>`a`eaaO^JuN)*S|Jc2!0Y0LaA2!aLZ(=Ed<$ zD&d`r`n8UgnTx)G3dWVTIwXvI4iVJ)I2{J9FSUu@CS%4hD54_8Bt&^Emd68*sYF~a zqs+HQrC~={+KVh31=7u3f-yew7*fcwR*p%5UYLUOrt6Tv&a{ zlF!xjx44I6K&r0?0H^!}oo~!vbgQTEThGn_43VEc!n6TfO`WBlZsl)V&H4z(wdsMX z+$McdF~5b9RESo_KU4fEuZiSA^>DQ{LMJQDtPcMy;{KTY7vANBsHsAo#1IFhB6YLC z$J2joQBL&wO%7kPut{<%f{?^do{G1jeEQbx$Kte`J{xgvV6#fb!4G9rI7m^>2d6mL zLf>d}?mx^b&`fndHP|OVf0VGRBWWioDFs1&N@>LmD88uz7_@Te8EX1Vo!z3zo$~XbV;UTToGFcmIm1GY%!SrX9y=WJK%z+ZnF#TFWqm9<_vf`n=&-Z z!HV#KMB|?EEK~lbI}~Z3Z!z1EDP`wcF`GE*VhK`>@hh|oCwJzj7pMqS&k+Xb7-p#e zmE_L{nTCq8?1NJM-}$Yzj6oY_6s^1))Wx5!Ap- z+Yt(gadNkkFQQdslrLGq6nHd?DZh+!W)$3&BkO=EZ;o{DKECY6?^1KHsemDwOCk3G z&$cZl(uI-AGTQD3f}xCXU#^NoR-Y|y1oycg&Enm)4HBq;P;%# zFHo*5WBiBZNt9DpE7fRz^Y2-UeO5bz+~xpuJR{O3NnhZ^fjKW^z2ffC#-!l=9jS@oRtlG0&Dq-XzG8(R#l*j!n{ zZKF>k8$iRw0!Mf^N76QGX6?=&B`-a$Pc2Dn@OWJP#0T|`b7MIRb78~<%Qn)#0La_4Nr^k{0rZnIWRs3{%}s8LnP#+u(Vdrx=0SV z8|*eUZhjr_dR-%IhF8>UtweZz4(o9ZU?k1`EEb}oy+y+yNZZVEmHSg+)NY|6&2L)4 zk^eU^htd46WyOqD*THgygGs?drxe^A^)r`Bv?1BsXvJPtRam3=?lB3o<}X^Lh*x}F z(2oRP!pnDL?{63dz?hJ$0uF2lHrc8x0g75o2IPGg&Y>?X@eo@|P159EUw@kYafRXA zSm+Jb{+i2IOQoy}`O_w|>&cG{tY zgoGug#6clOq!3+^YjqZbf#r?$qfK_pmDLD!Zyn8@HjmvAp9eRkTQ`;U88qIrZ-JNT z{l{uECAGybrR1(#VTIgP_nQrG6Ku+Cs!QUdPrAQMconT9ZE>? zi9LayMXpwIV_6h>KrO-*IFb_(`K+FHd^RtgYWlr0s&6&TUBs?n(flyFejiLDX_#J_8= zTsboxa^3zz2a-XC`qg!y@zu_&DM&knm|5!8tI`6;jky}@>36!vKBF-v(`6C`W~CnL z=+%kc20-Puy;!IR#za(?;LvJL*u|dGMFOAd zy1n1mA#~eZTuMIqqL?*+=*fcIo{_ck7=FndszYaYt1=);+h=^v(g#vxs{p`jG1#KR zuvf_J)MBoR-s!FP`}Y`%NiFJ%c$C81%sHS&Ox!RRGuD*rvtkyes_IXew_m{h2H3~)8r_(SK+mG&aygT1&gm1&+KC&zz?{;giDfN9oR ziQs=vzjHno1cLNtP8Gf4^nXXZPPW|gVoj^6rRNGeo-8^D@t(S&FI$^|o_y_reHSA` z=cpU_(OjtVkU<3o$p-zsevOjCmdf7XN=CeqHpOCB1nUChGu&9675L70 zzQwPaUN&$2_WPTXEFM~~Dfo<@@>+SO*IvS*Ac=!?OW;OMjYudpfy*v@>ji4mLj(m-t5@9$D@Gt^X&$U z`Z}2mlSavpzb*?r!kMdXP8M^8ZyPrY?c*tZo6MXNb^QER6HMoP(8%=|)=LO6QByTofw%sI+$CSDku=Jxv{|r39hDepLbbO(L)t|gb zBd`>1aM=}(Oyaf9der|Z#79Vv5jYL!Ft~*XU^x8>xhCf34uYKT>$sO`aZaY^#<8%< z_W02zzGo3K4UtIsTQ1)QKVtRV@Gy&v#d>R5J_AfM#c_ z1U0nk);RSQQnlY_nY^YDa0p?6^u>}*ns^)-xkOkRhH`*`T37kkUkLaQ#(X9kP=4hg6O1&Fw=|xJHvRkVEAXLI9k@_6 z$)Xjlr0rNs>*?O~n&q4Wm-HEddub| z?QxC>F@jUvF!3H$D=5(j=HSEV18dc0uL$-@S--&=)^=S2*1Gw}tce(&9QGulI!}dB z0siz2>#3Pr)x*&(%~qM7{uIJ)E^L;!qu69@8GviRy4MPDq~1gS*LB$ggW zgMoA?;A>Y=wTlKu|M@M0Y{~+FaHitQ_rFFRuGgQ>RXVX_L+}&Rq6gBxlvj`~nUKD9 zy$ZPi8;pDEnxuU(Jl_RJIbuA3RYfB`ZwsB*!qo*tR^X@8JG`I>sJvKy zo-Xhf~`m|8{m>@Cz{LV>_>fEi3lKtjXg1xtiZ{NvtXB&0WTG$;QBO1`Lwr>wZ!$)NGF+cosA`8R|Nq2 z6KxV=8B{Fp z^MN~Z(xcyVE48uaT7JzBPmGiWkCHFOYKqMWVU%qA#%|o-QSK5OwZmUQ=X*J(Reum6 zwvC&%PM=QW71=ASEMmG-O)w>&pfp@{Q00C`NIL3S0>uei?X`#!W>%CuCFA_1dSSPK zv(x43RN5}Vb{;Sj4@eCIv?go+cmfI~V9!kxEdOk|WlmM=x{BH4ecN<)Jy*+{Je6cv zMVIvt0s8lcmGI7?m`F|<zXi*$M@0?ur5f}45FC2N-IcHK{Mp`W8j;)Qn- zO@WHnoc2gZ`$!D5$4$1Pq+wOk$IfgV=bBz_v@)W(L_9KSm21C_B-?eXLjRliY>fUw z*I}@eu(t#-dmcTWNM+!%{lu{MTgV3icX@(a*T~9GHX^dxUlTS@UOP_!5TGpu*d9!i z_RmmpYE>$O7X%KqCw?n7nuRTb_z@EwcMiV`*mMxf69w*g8n2o!nx6NXrG~qy&h>AH zmf;ZD*%K}wMeLzd#yWd*pchDA(n)CH{`U0MS0F<;Pa}qL|IvxxqRY(e+b88i9}^{? zlo(LSaGAXW9)ILpWgdGwb_3_#DeJdXLUec?)9TDt6l!&a&SnO>`SA_dftgX-l?_mm=!-N4Jaj;{u{&jNet)zyL2F@pIJ zgeG?2orvVwp86QxWZm=$=h)0qdGReEI%dDa$#pRsewnh@<1--m`Xhr9X11g{av~;T ztgW^AAdTgUa(*EQkmV(E0iRii~7z zt0<7b_O`KZLG~#`3y)nv?G3J}P5_oW5AOg*+1YiR%na6#DF^2$CVO+oa!&V_QbTxu zl`DJudZVtS4@R0Mf%)}AZCPxa&gT!YNpk`DxN7G7{$RS;;PQeZYYspOPh2i`R|7fc zi}_lm*M7g=~m`1dBAP+@O;0v#C8v!3J>l2=W|ue=DQf2KQT%4ELx~RGT_m? zii1$z))okPdjNFda{75C7iH(g1-@ZK^Gi!V0 zV`VQQbzmFaG+HBHuTF5MiIUYB{6)@PiBKuTIef|@alanfjb>3!7J1>rw9-fBYL{{= zMSQ=$wHRB^Et-eXBjYe1rV2T!#$4d=_n^~mmV#S-LP+C{7-a!Q;M!ryFgZVcjiigC zn(EG{{SiKPe|aZ{(Xhu!(T9gvFIYx>OD%37xYRi}hqiC}YQ5C+gGkdR8xBtw=&z0}20*(qBQ0OW8cNeg$ zg73OKs{ACj8SJfdpu9id+lHu@A>ahg<^W)uW+W-&tC94%@2`^r`|b@x;3~^0=sQXD z$C3k01DWt-Q!Rj2SU;&74tHFLAuxDtbq!caL4T$MeEu+UC%*s}|NlK9y%5~7^z9!i zv6fKpyAQ#HsCUGL;^J3XjcaK@%0S|fK|_gB1jcap;vpN@EJhtk@14vL@;^MeTl@!N zUW>7`ka)*ZgbQH#V=I6V#*W@LRJ%&^H>1?^vjA0w}coF%HjHOS+ z4w<8FJl0j#6cpI0{{F0cH^7BcJN%0ekW{oP@^MH-w)XcUQQN#tPj~t z7d-z0|4BP4NihNeek-tyWa0Bte}A%|GsPRH-TC6NTyE#B@Kd49+!#l|seHP)+LdRT zB(>=bAm;vkk}Px1CdV!#xaGIgj-{X=)`?`13jL14x>iR(sT_Ku5GoJ1}W{FVPCJ`PoqcazFCKXbQxosm-`vT zhg5C4tsv>^FaK~ls$a5hJvFj71o%67I-G`b*-yKp+2&;gGYfbtoR`@5j9*fA@^8Kr zE4qWC7bqrg$#&)v-ha?4qe&R)12385U{uZA`5aY;7dyCw6i}fS9XU11j2cJ zMpkA_dH`5k=8~1(BzV-~8ZnAqAeox>%8FQI_oLa9xepEe7L4SN&uN;kQ^gNu7);G- z#VRE{ZU70H{3i(<2T-#zypbqx)cor}O4{mTNxy&vrOd=Io7oHiH><^0UQ zp4e=N$!q^XN7vz~qw(~3nGAPF=z_|O(|&ZSAq;Atk?UAB^j{VLX@r;|&<7Xp>(qO7 zm$8(|T^YRqyVW89#LELYlHNrekgHiBXI6Bh=e&o%E*4E%Jz3@Y^q9=WzwOqY*CQ!W zqS@I_er=;c^T7S|%tg#RPiFmZa+Kly=9MFUwc&PUvBTByE0^ulMB~rnov^HTW;c~A9Neoi5?ou{!I=wxh{ewyW|@2aj#Y{Z=2WKO%tXr~N=RkMA>bq+bK8wG{a~`W&bcnD;pz7)E3QNgkLj{ouEFEcIY(GD$0A2p0D$D< zYWBywY&iADLtfe3Hl_j2zwBnXchB4h_Qe9zW$b%@NZ05$!guk)gHUF2bk5@s3F{yB zEunS`4Eq}wAF+m#huxE0YeK%&cm^K8niB$W$c+?ZQUht+0;s)eQm?~em*l*(>-M>U z*dgatE2uZotg(kiG0=Lvc9}x~)JmbWi!sex-{$0^+^tGQB+sg4^`H_JFC;`0}Gc==Psj)gb#*q zE^m~nO4G~H7MGx&&2kVA68UAUY2$o&=6m}l=`hs_Wy9*h~Ro@evg-Rbg4 zQ~s#eF0!)|&Hb|d-RI?Duc>2`o&H-$s-X`_guwH^J|pdS2~^8Kn#hAlo2nEcmzdVJ zJVcej5fW4rgNLk*DLcx^%#xB<+30g(7IVpg4Wal?hMZDUJpm};Yh2Dh%{-$!%lyD zny+(_JFe3$Tk9T)NRRbFU?)+4hhT ztC}~28R)F7OkdD1m}CYY3C=kpbg6AU!kntc7|fJXYfTGgF&`bwetK6sOOUp|v9^{k zoTpy!ew;&#*}zYLUQT;PrdhAisD+bYCn!)9^X@j`Z2~9dIPZsYx}6SGXE5>PN*iQ} z?|N$)+|*9HY-9t|EK}ti&TT+YAbB3=kj2_T8Li4vmFaXd0wT(!9iY; zo`5na8xaBRCGrk)qhFk!=CVi!`8i`^^6#8l(xKnlLtlR6`2x+(8QYBEi>&Lhc`Wo+ z6hEm!U1*7}?k9OdM$ z@$;*Wg11)`>P4TW>acxFW;eV$HDibA{xhWj*+2P8vXGQfr7GaDcn(~HUnl~xRBI)t zslm!$eg=kl87GaX^bIkI6rabmc_w~wxS@$+I0J4Dec7b)GR6IP|Vi{8alKoTmBaR&U`yE{}%8lssES4 z-(>DsVgZD6)ah;D=gkQyF@;K`LC^`I&_o6h_b3QyuPXT@>Bu!ZJlCFOr~zAPkXM| z@(@ywa(_VD-&#Hk(OhP!%rbzl=qNyy5Vf4{{~c`+`H|Uy&rsy`~jp(H>z9>lWu;DFF)?O}2B5 zJeU6ELl8_r-~(PSBFmREFUNqK?aYmhI@-P4akQt!nM(gZyuEc;liIe2;6bz4lsrzwdtE-|slS@1Hq% znB#z%`@XL_uk${y3SwOS@AmN@i=^Ji8q70I;T;u+wADO4d!hZ(vHx{?XfKyvm`1s` z$I*(y`LuCqvW5CugHFur{ORfRfv4Z>bkc0{vc^U;;^NX+->-}+cJgfw zGCp6dWM^y45?ftaYn+99v9=3d>hoZp2cnqAy9^9L2v-K^`nPyvY+U5H$4|4 z)*X+1$`h8Iy-M`gBP$&yn07c50KUV7*L9fKAtA&A04R%EmJ{iUr$#-d_*;WeG5?uP2Hj<_**WE8cyq`Mrb_hIR*iF05@_!y z=exDcc5jbxt(;dV*Jc)GM*mPxtM~IwogU3XSGvkG6{|1XH!_C`&biQ?M+3`!!}O8_VePt_4>Q@ z)Tslu+zYZ7*|if&JWgw=$;VxaQy2SIW;5p?>!a!4Wq;OiekmN2S)@lgxg!O9ZVJH z@A}fKi-2@~_E{c1B2j+4KryG6ut)2pUy&xsg9L(R^FC_K@tyRjfSQP6^Eb&3T^<=fHFxO5 zDBl4BE{`(8alZ@(7TKoHxX(5&gh+fUpHLN8UVoB!KD@{2-Uz!l^{1TV4^ls%S~Zpq z(|@)e(#*v35ivE_^t=k+=#6oXSx(BdKiN>A^|opLCC|3Lb7pb9`Dz)batHmwu!#-m6Xut8w1b?d}5B^?Bew_zU&lTE6 zHUmEKst=Or=veuchyFCW9?yWP6lmnjU2vCnWi27AHNRMTR?}yFsmDwz=JIFWR0tEe9( zHKOc&v>Z&Z05hH2ku;J@aKL^BS42;d16Ra2WSnYmt}#7pcP18z-1QZiZJ;72ALzgH zc#SqlMVNRK8{_l6+YF6+Byx+OR{?0}UpMONR{QPl1V@rcH)p|m0x0&XB#5ol7`ys2 zGHSa+3Km^9*}hWtM+u2LLNjG5BFWk~pS#I^k%?)DA{9&(aN0}SAWi47`-H-FF1vcE z36h_%;a8vuLLgdAD{=05*1Zd9L4S)oQu?hZwEPmr<06!l*z58xBE3aI?;Y)pP zObq+Zl+SbE?xwP(kI^kG#p+TWg=i5~ijeSgUt?Ts*gZcF9;<3f9-gtT-*VT908gc8( z7ES9*D3V{+3(C$WZd(QG7=-+Ox##zu!9QR91Veu*rOpXB4;=9L{#{q(gk%}~VfY-r zn?D$B^*dd{Rlu(Xr&u0_-VRb_2|@isKk_%U^EZKP8oI?Vss$=z^O-Ep>V+}9;lBG2 z`LE6?n)wYze8labdpN({E&KN)-AWjB*@Js9^j0 zcDibCOX$mgCo}2F`Ww><=kJLcpyep{K8O8}uWBy`7%&{K8@<(e$q5PL z_jwNh{>2Q=UE=@nhyUfRLy}+Net&)aQ!qPmEP$I!K6NZ60p#w(PWPK}02SY4(Q=$W zES?{bABARoim?8@p#0ywx-$LRS>69vI~#FWw)T^| z>A&`pf3u%I5L#=1XtbM_4=6ZulRK6^0@fbu%(fX1SbHK-)<4%?7Fc`ouE_BJmE`_! z);>@7%VHh~1wzV~Q%v#z%AH9ec=w}_Bs`r^EN^ZW?>pZg(ax0G@0;~h@tEVv#Uo0e z>aWj~x&E6t`0sASR~$H~AJA2UQh~$@FV@2hSgqz+>PD-3%V6hN$@)LO@sbFt5Fj)u zQuCJZ4}<;>EC0t^e)CeT+Vq{1;37k3`^K-g4pj&FuhA@~@g?|F_%@@as3+1U8$&vJFiWzUk8yZUn=8D!+1buh#P*444+oI8za}#KJ)i&ISm`|DZ_Y;M zUz`o~bCzTS_V|zMU*0mttM{up<9B6=c%=aXOo>G&^TLk5B;5rBbkqqiHV?`YYrQQX zHS$7?5B4`@fTwbuYs+~0u?>4$c|~NFI}mNHF>?PQGWS34J&F?DFP+kJ@)|Q@4}f_e zZo`v^xeVs<`%n#NRSc#+m}&Ol&E$6s5?*_}eESiQhXoDk@uinLtoNCn8+eA@W`6=Q z0br&jvI@hQ=TZ21$0i#YC37-S>Ri@vv3f~EgueTIK>V1J;>-WuqSl;$fsdjn|Kjrj zQLwOaJ0mh4n@^pc5>~D*;&Ensu)wDm)ys9*lhi_g$7)5}0#CsM*c*M({>`%e80&cTQ%P3C? zf$(?n+rCS)|Ipi&EH5~yM}G8kTuDLOwg~(%%HQYtT*VUasEp>@TByv?Bg7lqYkl|a zJU4C4eMbhW{&Z&1pyRs+kDvdK|NgI^($T+FkAJdkeEYahwG-O3l6BcGfym_Vs2>Rk zfn3hW0-xpRmgDGRd$ki(#3p*JTAuWLj!lzBNlCQoMiKWy?}hrD_a)wYx6E(DpFLT? zXWnI{R&A(2ACcd`m8b=^fpg-^ zKiNRyR~X^3v*-Q6A^q$%|5z+0c&3EL+rJze$B@>^GTryE<_muqecj}w#-YmyAP$|| z(n86aY<+Cu#`2%{mx1K2B|D zK>JALHM>xzkFs#%;|yHK?WPR2^_h3snPczh&M3A;B9aX}QFs3^3-e&Q z62((A-F9J?8$K89iRzhcnJ(j%%DU?$&vurHvs16TVPhkT90>1O1tunvkdU-)Z56tc zKm3RnD)Ez#a(K{?z14=NW7+;wP0gc;i3xjwOe$=zk%Lu(h+(z<|sUa7djiog`^OtdCV!nG8 zZ(EkmdmGW&Ykqj?Pfzw@^;rH@CsgR3C?HsP#4EbQy}aX($(nL@znl8C#69W|*Qn=0 z(-^{`)wK_qC%UyIoaJ%0WC@q<%35}U3OvNG+8@$N{nf`N29uZ0EaCCZ(iHZQ>? z;aVzlFz#L~J|8y6^yzz&gmbwSHiD3`ReQ1EJMxEgrRby?A=y#-aw zkFWhRiAIIi$7rv${L)US&F)-Xo%Pk-F>;e?U1bC1hk^^k8vV5`GdkIN7wOSgVqc$j zi%}m+H-~hJg^YTjLxdDgkw%>ShP(n>i!#H5^{lw{rAma}9n0+gZBLsF*t)yBmoBc` zI&ww}8@`?jUOFnr`|LXlhJ}YG!uy|Bb{VIBy;C}ojEkSl8+Fc~5WPN_NF~k>wagR$ zi}!2_#jsd`{rG`y*K{OBd-XDEXh>1$_y@HtDzcMa#1D`Sg@iK7Z2!f?|J?&Wdg)j1 zP^#Mr6>QPD=Hxak9g59NAMXc_X1tcu<7l8^ zp3Zw6mD8E*bNV#!q_{2XS3-OAjML&Pg=FsGn6;fg#x_EtLkB}?!YLV*2x>J)RNSo8bXdP3`&WrVYMIo)rnh&VK^YLa)Q31Y)Z}>_Q4SJ& z>W8yd%f-6BUHy}va*CyNBQqA>=~7j+?&@kGn#nxQ_a)pCDb~--6Y5Be*iuW+6}>a` zY%@unR_rO$Kiwh@_<|a8NOmwx3Zc=ow*4}Q8}`ZaUZEr2xhwC}g~m8$!2k&MiIx3% zz4Zuth4X{v49`iMT|u{skDNSIXV?#-j9?=?P9EnS6kF2lJNJD2SZb3#Elxx{*z|08 z5f~o#-EqM|8^rQh)xV-=^)-m9Svn5h|6%LYon$NDi*aLELV>^>Bjux^;@zk*1cv)i zGOeA3#Vf|VB<(T{lF07T{>hzR?vA>lreyx;?qBwd&(D_*c^r}sZh10GG^y1*@Y-9X7Q9$UttG%=k0#DDz{(_+i7KSxxgA7Ax zW=9nX96C(wCs+f!)L%QKzs|eq788-Uq2{u}W1lrDvI-bgub?r^9F03_5WbyvRNauN zYtaQ?qcF+ivpB%tS{~k2#+84}X+!j_Mu+vK2eu8swIoKNC+X=(;MqBQx<#4asoQsl zXNH}$dg(9)e=%-zip`8w&nZCmj&D@pi3=0xDtnZRrZH>F5qi12mEH&;xA68N- z)64#GE}fi9#6Pc7M$$rTtv+?ZuLSCnOfbs$2 zH5SfyWvEtwHS|6HV^*u7(g3Xdq1qO#$M1FM2c^9hOwbM;hE=ynXYToQ23QD5R1Bkr z4X~;aPkgR-#Yjrb{wV7qKBT=?EaJT%DE2HODyn_o*~G#E{h90ycPhi#yrFMaC#cM_ zf-}jRJ{-ctm~vGayQizF8URE~+0yb)omSU@T%~8R2hAG4;;jMwpSj1_g2v0tza~Mg zMHuq*a7A9kPvO2tf7$VWw^DRJ1i|kKz#z<5|v1(q>qs$EtY0?{_=ccoMKE^tu> z9&}xle`k0!)v*FL#p?QCTlrY?HFynGpTN=tpWxk1i1H4Yi~Lo@7|VxRhm?|}_NBrB zCuP^=%gus;cssAUShy}Jm8A74o)tOAchVihniRZzH7Gmn`yelx>#>IINOzJlx|Hxe z_8D8Q$V^+qz#EZ>`koP}pxEwW(4?Gb;Mk

Le|(QdL=W^37FJ*|hz|0-wDpTN-h) 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 z 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%0AT{TQJSFy^OkzwmKgjT`5WI-XnvTCickO0@k z{`BmJ=4U&_&OYl!hd>q^>CRN)of}W~E%Shfwh5_4^u_U⁣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

RM~1?zj`!*sUPMb@S6f*vrQs@xswTNN) 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 From 2edaa2ea5ac9cf51fe1df476b3bfe44b103ec4bb Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 09:13:29 +0200 Subject: [PATCH 155/214] api: merge the how-to page into the section index --- content/api/_index.md | 342 ++++++++++++++++- content/api/howto.md | 345 ------------------ content/changelog/2025/03-07-rust-sdk-0.13.md | 2 +- .../05-28-oauth-consumer-new-interface.md | 2 +- .../2025/06-05-api-tokens-console-tips.md | 2 +- .../2025/06-05-oauth-tokens-new-interface.md | 2 +- .../changelog/2026/04-09-clever-tools-4.8.md | 4 +- content/doc/kubernetes/operator.md | 4 +- hugo.yaml | 2 +- 9 files changed, 342 insertions(+), 363 deletions(-) delete mode 100644 content/api/howto.md diff --git a/content/api/_index.md b/content/api/_index.md index cedd7de49..a42cc5cdd 100644 --- a/content/api/_index.md +++ b/content/api/_index.md @@ -1,15 +1,19 @@ --- type: docs linkTitle: API -title: API -description: Clever Cloud API documentation. Learn how to use our API, endpoints, authentication methods, and how to access it from multiple tools. +title: How to use Clever Cloud API +description: Learn how to use the Clever Cloud API, its authentication methods with API tokens or OAuth 1, available endpoints, and how to access it from multiple tools. keywords: - rest api - authentication - endpoints - documentation - integration +- oauth +- tokens +- developer guide aliases: +- /api/howto - /doc/api/ - /doc/openapi - /doc/clever-cloud-addons-api/add-ons-api @@ -21,13 +25,333 @@ aliases: - /openapi --- -{{< hextra/hero-subtitle >}} - Automate your workflows and connect your services to Clever Cloud using its API. -{{< /hextra/hero-subtitle >}} +The [Clever Cloud Console](https://console.clever-cloud.com) and [Clever Tools](https://github.com/CleverCloud/clever-tools) allow you to manage your account and products with the same public API you can use for your own services and integrations. This article will explain how to connect to this API and use it. {{< cards >}} - {{< card link="/developers/api/howto" title="API overview" subtitle="Understand Clever Cloud APIs, authentication and available tools" icon="api" >}} - {{< card link="/developers/api/v2" title="APIv2 reference" subtitle="API endpoints for users, organisations, applications and add-ons" icon="endpoints" >}} - {{< card link="/developers/api/v4" title="APIv4 reference" subtitle="API endpoints for billing, deployments, load balancers and logs" icon="endpoints" >}} - + {{< card link="/developers/api/v2/" title="Base v2 Endpoints" subtitle="Our base API endpoints with users, organisations, applications, add-ons, etc." icon="endpoints" >}} + {{< card link="/developers/api/v4/" title="New v4 Endpoints" subtitle="More recent API endpoints with billing, deployments, load balancers, logs, etc." icon="new" >}} {{< /cards >}} + +## Request the API + +Clever Cloud's REST API offers two authentication mechanisms to meet different integration needs: + +- **API tokens** provide a straightforward way to authenticate requests on behalf of a specific user. These tokens operate similarly to passwords and should be handled with appropriate security measures. API tokens are ideal for personal scripts, CLI tools, and scenarios where you're accessing your own resources. Use them to request the API Bridge: + +- **OAuth 1** is designed for third-party applications that need to access Clever Cloud resources on behalf of their users. This authentication flow allows applications to request permissions from users without requiring direct access to their credentials. OAuth 1 is recommended for public applications, services that integrate with multiple user accounts, or any scenario where user delegation is required. + +Choose the authentication method that best aligns with your specific integration requirements and security considerations. + +### API tokens + +Clever Cloud Console allows you to easily create and manage API tokens. + +![Manage API tokens in Clever Cloud Console](/images/console-api-tokens.webp) + +[Clever Tools](https://github.com/CleverCloud/clever-tools) provides a `clever tokens` set of commands. This feature needs to be enabled: + +```bash +clever features enable tokens +clever tokens create "CI job Foobar" +clever tokens create "Quick local test" --expiration 1h +``` + +You can also list or revoke tokens: + +```bash +clever tokens -F json +clever tokens revoke api_tokens_xxx +``` + +Once created, API tokens must be used through the bridge URL: + +```bash +curl https://api-bridge.clever-cloud.com/v2/self -H "Authorization: Bearer [API_TOKEN]" +``` + +### clever curl + +`clever curl` is a wrapper around `curl`, it supports the same arguments and handles the authentication automatically for you using the CLI account you're currently logged in with. It's a simple way to make requests to the Clever Cloud API if [Clever Tools](https://github.com/CleverCloud/clever-tools) are installed on your system. + +```bash +clever curl https://api.clever-cloud.com/v2/self +clever curl https://api.clever-cloud.com/v2/summary +clever curl https://api.clever-cloud.com/v4/products/zones +clever curl https://api.clever-cloud.com/v2/organisations//applications | jq '.[].id' +clever curl https://api.clever-cloud.com/v4/billing/organisations//.pdf > invoice.pdf +``` + +### Official clients/SDKs + +You can request the Clever Cloud API from multiple languages through our official clients/SDKs: + +- [Go](https://github.com/CleverCloud/clevercloud-client-go) +- [JavaScript](https://github.com/CleverCloud/clever-client.js) +- [Rust](https://github.com/CleverCloud/clevercloud-sdk-rust) + +### OAuth1 + +If you have an application that needs to access Clever Cloud resources on behalf of your users, you can use OAuth1. This is the recommended way to authenticate third-party applications. + +- To manage OAuth tokens linked to your account, use the [Clever Cloud Console](https://console.clever-cloud.com/users/me/oauth-tokens) +- A complete working example (Node.js) is available at [github.com/CleverCloud/oauth1-example](https://github.com/CleverCloud/oauth1-example) + +#### Create an OAuth consumer + +First, you'll need to create an OAuth consumer for your application. This can be done in the [Clever Cloud Console](https://console.clever-cloud.com). Go to your organisation, click on **Create…**, then on **an OAuth consumer** and fill the form. You will get: + +- A **consumer key** (public identifier for your application) +- A **consumer secret** (private key, never expose it client-side) + +You can also manage OAuth consumers from the CLI with the `clever oauth-consumers` command set, which covers the full lifecycle (list, create, get, update, open and delete). Use `--with-secret` on the `get` subcommand to retrieve the consumer secret: + +```bash +clever oauth-consumers create my-app \ + --description "My application" \ + --url https://my-app.example.com \ + --base-url https://my-app.example.com/oauth/callback \ + --rights access-personal-information,access-organisations + +clever oauth-consumers get my-app --with-secret +``` + +> [!NOTE] +> The **base URL** you set when creating the consumer is important: the callback URL you use during the OAuth flow must match this base URL's domain. For local development, register a separate consumer with `http://localhost:` as the base URL. + +#### The OAuth1 flow + +Your application must implement the [OAuth 1.0a flow](https://oauth.net/core/1.0a/) (also known as the "OAuth dance"). It consists of three steps: obtaining a request token, redirecting the user for authorization, and exchanging for an access token. + +{{% steps %}} + +##### Get a request token + +Request a temporary token from the API. OAuth parameters can be sent as query string parameters or as a form-encoded body: + +- `POST https://api.clever-cloud.com/v2/oauth/request_token_query` — parameters as query string +- `POST https://api.clever-cloud.com/v2/oauth/request_token` — parameters as `application/x-www-form-urlencoded` body + +**Required parameters:** + +| Parameter | Description | +| ------------------------ | ------------------------------------------------------------- | +| `oauth_consumer_key` | Your consumer key | +| `oauth_signature_method` | `HMAC-SHA512`, `HMAC-SHA1`, or `PLAINTEXT` | +| `oauth_signature` | Request signature (see [Signing requests](#signing-requests)) | +| `oauth_timestamp` | Current Unix timestamp (seconds) | +| `oauth_nonce` | Unique random string for this request | +| `oauth_version` | Must be `1.0` | +| `oauth_callback` | URL to redirect the user to after authorization | + +**Example request** (see [Signing requests](#signing-requests) for how to compute the signature): + +```javascript +const url = "https://api.clever-cloud.com/v2/oauth/request_token_query"; + +const params = { + oauth_consumer_key: CONSUMER_KEY, + oauth_signature_method: "HMAC-SHA512", + oauth_timestamp: Math.floor(Date.now() / 1000).toString(), + oauth_nonce: crypto.randomUUID().replace(/-/g, ""), + oauth_version: "1.0", + oauth_callback: "http://localhost:8080/auth/callback", +}; + +// buildBaseString and sign are defined in the "Signing requests" section +const baseString = buildBaseString("POST", url, params); +params.oauth_signature = sign(baseString); + +const qs = new URLSearchParams(params).toString(); +const res = await fetch(`${url}?${qs}`, { method: "POST" }); +``` + +> [!TIP] +> For quick testing, you can use `PLAINTEXT` with curl (percent-encode your consumer secret if it contains non-alphanumeric characters): +> +> ```bash +> curl -X POST "https://api.clever-cloud.com/v2/oauth/request_token_query?\ +> oauth_consumer_key=&oauth_signature_method=PLAINTEXT&\ +> oauth_signature=%26&oauth_timestamp=$(date +%s)&\ +> oauth_nonce=$(uuidgen)&oauth_version=1.0&\ +> oauth_callback=http%3A%2F%2Flocalhost%3A8080%2Fauth%2Fcallback" +> ``` + +**Response** (`application/x-www-form-urlencoded`) + +```text +oauth_token=&oauth_token_secret=&oauth_callback_confirmed=true +``` + +Store the `oauth_token_secret` securely server-side — you will need it in step 3. + +##### Redirect the user to authorize + +Redirect the user's browser to the authorization page with the request token: + +```text +https://api.clever-cloud.com/v2/oauth/authorize?oauth_token= +``` + +The user logs into Clever Cloud (if not already) and is presented with a permissions form. Available permissions are: + +| Permission | Description | +| --------------------------------------------- | -------------------------------------------- | +| `access_organisations` | Access organisations | +| `access_organisations_bills` | Access organisations' bills | +| `access_organisations_consumption_statistics` | Access organisations' consumption statistics | +| `access_organisations_credit_count` | Access organisations' credit count | +| `access_personal_information` | Access personal information | +| `manage_organisations` | Manage organisations | +| `manage_organisations_applications` | Manage organisations' applications | +| `manage_organisations_members` | Manage organisations' members | +| `manage_organisations_services` | Manage organisations' add-ons | +| `manage_personal_information` | Manage personal information | +| `manage_ssh_keys` | Manage SSH keys | + +You can retrieve this list programmatically with `GET https://api.clever-cloud.com/v2/oauth/rights`. + +Once the user approves, the browser is redirected to your `oauth_callback` URL with the following **query string** parameters: + +| Parameter | Description | +| ---------------- | -------------------------------------------------- | +| `oauth_token` | The request token (must match the one from step 1) | +| `oauth_verifier` | Verification code to exchange for an access token | + +##### Exchange for an access token + +Exchange the request token and verifier for an access token. As with step 1, parameters can be sent as query string or form body: + +- `POST https://api.clever-cloud.com/v2/oauth/access_token_query` — parameters as query string +- `POST https://api.clever-cloud.com/v2/oauth/access_token` — parameters as `application/x-www-form-urlencoded` body + +**Required parameters:** + +| Parameter | Description | +| ------------------------ | -------------------------------------------------------------------- | +| `oauth_consumer_key` | Your consumer key | +| `oauth_signature_method` | Same method as step 1 | +| `oauth_signature` | Request signature (signed with the request token secret from step 1) | +| `oauth_timestamp` | Current Unix timestamp (seconds) | +| `oauth_nonce` | Unique random string | +| `oauth_version` | `1.0` | +| `oauth_token` | The request token from step 1 | +| `oauth_verifier` | The verifier from the callback redirect | + +**Response** (`application/x-www-form-urlencoded`) + +```text +oauth_token=&oauth_token_secret=&expiration_date= +``` + +Store both `oauth_token` and `oauth_token_secret` securely — you will need them to sign every subsequent API request. + +> [!NOTE] +> Access tokens expire after **3 months** by default. The response includes an `expiration_date` field (ISO 8601 format). + +{{% /steps %}} + +#### Making authenticated API requests + +Once you have the four credentials (consumer key, consumer secret, user token, user token secret), sign every API request using the `Authorization` header: + +```text +Authorization: OAuth oauth_consumer_key="", oauth_token="", oauth_signature_method="HMAC-SHA512", oauth_signature="", oauth_timestamp="", oauth_nonce="", oauth_version="1.0" +``` + +**Example** — Fetching the authenticated user's profile: + +```javascript +// buildBaseString, sign and authorizationHeader are defined in the "Signing requests" section +const params = { + oauth_consumer_key: CONSUMER_KEY, + oauth_signature_method: "HMAC-SHA512", + oauth_timestamp: Math.floor(Date.now() / 1000).toString(), + oauth_nonce: crypto.randomUUID().replace(/-/g, ""), + oauth_version: "1.0", + oauth_token: ACCESS_TOKEN, +}; + +const baseString = buildBaseString("GET", "https://api.clever-cloud.com/v2/self", params); +params.oauth_signature = sign(baseString, ACCESS_TOKEN_SECRET); + +const res = await fetch("https://api.clever-cloud.com/v2/self", { + headers: { Authorization: authorizationHeader(params) }, +}); +const user = await res.json(); +``` + +#### Signing requests + +Three signature methods are supported. `HMAC-SHA512` is recommended for production use. + +##### PLAINTEXT + +The simplest method. The signature is the consumer secret and token secret, percent-encoded per RFC 3986 and joined with `&`: + +```javascript +const signature = percentEncode(consumerSecret) + "&" + percentEncode(tokenSecret); +``` + +For the request token step (where no token secret exists yet), leave the second part empty: + +```javascript +const signature = percentEncode(consumerSecret) + "&"; +``` + +> [!NOTE] +> The `percentEncode` function is defined in the [HMAC section below](#hmac-sha1sha512). For secrets containing only alphanumeric characters, `encodeURIComponent` produces the same result. + +##### HMAC-SHA1/SHA512 + +These methods sign a **base string** to ensure the request has not been tampered with. The code snippets below are a JavaScript/Node.js implementation example. + +**1.** Define a `percentEncode` helper per RFC 3986: + +```javascript +// Like encodeURIComponent, but also encodes !'()* +function percentEncode(str) { + return encodeURIComponent(str) + .replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase()); +} +``` + +**2.** Define `buildBaseString` — it concatenates the HTTP method, URL, and sorted parameters, all percent-encoded and joined with `&`: + +```javascript +function buildBaseString(method, url, params) { + const sorted = Object.keys(params) + .filter(k => k !== "oauth_signature") + .sort() + .map(k => percentEncode(k) + "=" + percentEncode(params[k])) + .join("&"); + return method.toUpperCase() + "&" + percentEncode(url) + "&" + percentEncode(sorted); +} +``` + +The three components are: + +- The HTTP method in uppercase (`GET`, `POST`, etc.) +- The base URL (without query string), percent-encoded +- All request parameters (OAuth parameters excluding `oauth_signature`, plus any query string or form body parameters), sorted alphabetically by key — then by value in case of duplicates — formatted as `key=value` pairs joined by `&`, then percent-encoded as a single string + +**3.** Define `sign` — it computes an HMAC with the chosen algorithm and returns the base64-encoded result: + +```javascript +const { createHmac } = await import("node:crypto"); + +function sign(baseString, tokenSecret = "") { + const signingKey = percentEncode(consumerSecret) + "&" + percentEncode(tokenSecret); + return createHmac("sha512", signingKey).update(baseString).digest("base64"); +} +``` + +**4. Build the Authorization header** with all OAuth parameters (including the signature): + +```javascript +function authorizationHeader(params) { + const pairs = Object.entries(params) + .map(([k, v]) => percentEncode(k) + '="' + percentEncode(v) + '"') + .join(", "); + return "OAuth " + pairs; +} +``` diff --git a/content/api/howto.md b/content/api/howto.md deleted file mode 100644 index 3c3ca229b..000000000 --- a/content/api/howto.md +++ /dev/null @@ -1,345 +0,0 @@ ---- -type: docs -weight: 1 -linkTitle: Clever Cloud API Overview -title: Clever Cloud API Overview -description: Getting started with the Clever Cloud API -keywords: -- rest api -- authentication -- integration -- oauth -- tokens -- developer guide ---- - -The [Clever Cloud Console](https://console.clever-cloud.com) and [Clever Tools](https://github.com/CleverCloud/clever-tools) allow you to manage your account and products with the same public API you can use for your own services and integrations. This article will explain how to connect to this API and use it. - -{{< cards >}} - {{< card link="/developers/api/v2/" title="Base v2 Endpoints" subtitle="Our base API endpoints with users, organisations, applications, add-ons, etc." icon="endpoints" >}} - {{< card link="/developers/api/v4/" title="New v4 Endpoints" subtitle="More recent API endpoints with billing, deployments, load balancers, logs, etc." icon="new" >}} -{{< /cards >}} - -## Request the API - -Clever Cloud's REST API offers two authentication mechanisms to meet different integration needs: - -- **API tokens** provide a straightforward way to authenticate requests on behalf of a specific user. These tokens operate similarly to passwords and should be handled with appropriate security measures. API tokens are ideal for personal scripts, CLI tools, and scenarios where you're accessing your own resources. Use them to request the API Bridge: - -- **OAuth 1** is designed for third-party applications that need to access Clever Cloud resources on behalf of their users. This authentication flow allows applications to request permissions from users without requiring direct access to their credentials. OAuth 1 is recommended for public applications, services that integrate with multiple user accounts, or any scenario where user delegation is required. - -Choose the authentication method that best aligns with your specific integration requirements and security considerations. - -### API tokens - -Clever Cloud Console allows you to easily create and manage API tokens. - -![Manage API tokens in Clever Cloud Console](/images/console-api-tokens.webp) - -[Clever Tools](https://github.com/CleverCloud/clever-tools) provides a `clever tokens` set of commands. This feature needs to be enabled: - -```bash -clever features enable tokens -clever tokens create "CI job Foobar" -clever tokens create "Quick local test" --expiration 1h -``` - -You can also list or revoke tokens: - -```bash -clever tokens -F json -clever tokens revoke api_tokens_xxx -``` - -Once created, API tokens must be used through the bridge URL: - -```bash -curl https://api-bridge.clever-cloud.com/v2/self -H "Authorization: Bearer [API_TOKEN]" -``` - -### clever curl - -`clever curl` is a wrapper around `curl`, it supports the same arguments and handles the authentication automatically for you using the CLI account you're currently logged in with. It's a simple way to make requests to the Clever Cloud API if [Clever Tools](https://github.com/CleverCloud/clever-tools) are installed on your system. - -```bash -clever curl https://api.clever-cloud.com/v2/self -clever curl https://api.clever-cloud.com/v2/summary -clever curl https://api.clever-cloud.com/v4/products/zones -clever curl https://api.clever-cloud.com/v2/organisations//applications | jq '.[].id' -clever curl https://api.clever-cloud.com/v4/billing/organisations//.pdf > invoice.pdf -``` - -### Official clients/SDKs - -You can request the Clever Cloud API from multiple languages through our official clients/SDKs: - -- [Go](https://github.com/CleverCloud/clevercloud-client-go) -- [JavaScript](https://github.com/CleverCloud/clever-client.js) -- [Rust](https://github.com/CleverCloud/clevercloud-sdk-rust) - -### OAuth1 - -If you have an application that needs to access Clever Cloud resources on behalf of your users, you can use OAuth1. This is the recommended way to authenticate third-party applications. - -- To manage OAuth tokens linked to your account, use the [Clever Cloud Console](https://console.clever-cloud.com/users/me/oauth-tokens) -- A complete working example (Node.js) is available at [github.com/CleverCloud/oauth1-example](https://github.com/CleverCloud/oauth1-example) - -#### Create an OAuth consumer - -First, you'll need to create an OAuth consumer for your application. This can be done in the [Clever Cloud Console](https://console.clever-cloud.com). Go to your organisation, click on **Create…**, then on **an OAuth consumer** and fill the form. You will get: - -- A **consumer key** (public identifier for your application) -- A **consumer secret** (private key, never expose it client-side) - -You can also manage OAuth consumers from the CLI with the `clever oauth-consumers` command set, which covers the full lifecycle (list, create, get, update, open and delete). Use `--with-secret` on the `get` subcommand to retrieve the consumer secret: - -```bash -clever oauth-consumers create my-app \ - --description "My application" \ - --url https://my-app.example.com \ - --base-url https://my-app.example.com/oauth/callback \ - --rights access-personal-information,access-organisations - -clever oauth-consumers get my-app --with-secret -``` - -> [!NOTE] -> The **base URL** you set when creating the consumer is important: the callback URL you use during the OAuth flow must match this base URL's domain. For local development, register a separate consumer with `http://localhost:` as the base URL. - -#### The OAuth1 flow - -Your application must implement the [OAuth 1.0a flow](https://oauth.net/core/1.0a/) (also known as the "OAuth dance"). It consists of three steps: obtaining a request token, redirecting the user for authorization, and exchanging for an access token. - -{{% steps %}} - -##### Get a request token - -Request a temporary token from the API. OAuth parameters can be sent as query string parameters or as a form-encoded body: - -- `POST https://api.clever-cloud.com/v2/oauth/request_token_query` — parameters as query string -- `POST https://api.clever-cloud.com/v2/oauth/request_token` — parameters as `application/x-www-form-urlencoded` body - -**Required parameters:** - -| Parameter | Description | -| ------------------------ | ------------------------------------------------------------- | -| `oauth_consumer_key` | Your consumer key | -| `oauth_signature_method` | `HMAC-SHA512`, `HMAC-SHA1`, or `PLAINTEXT` | -| `oauth_signature` | Request signature (see [Signing requests](#signing-requests)) | -| `oauth_timestamp` | Current Unix timestamp (seconds) | -| `oauth_nonce` | Unique random string for this request | -| `oauth_version` | Must be `1.0` | -| `oauth_callback` | URL to redirect the user to after authorization | - -**Example request** (see [Signing requests](#signing-requests) for how to compute the signature): - -```javascript -const url = "https://api.clever-cloud.com/v2/oauth/request_token_query"; - -const params = { - oauth_consumer_key: CONSUMER_KEY, - oauth_signature_method: "HMAC-SHA512", - oauth_timestamp: Math.floor(Date.now() / 1000).toString(), - oauth_nonce: crypto.randomUUID().replace(/-/g, ""), - oauth_version: "1.0", - oauth_callback: "http://localhost:8080/auth/callback", -}; - -// buildBaseString and sign are defined in the "Signing requests" section -const baseString = buildBaseString("POST", url, params); -params.oauth_signature = sign(baseString); - -const qs = new URLSearchParams(params).toString(); -const res = await fetch(`${url}?${qs}`, { method: "POST" }); -``` - -> [!TIP] -> For quick testing, you can use `PLAINTEXT` with curl (percent-encode your consumer secret if it contains non-alphanumeric characters): -> -> ```bash -> curl -X POST "https://api.clever-cloud.com/v2/oauth/request_token_query?\ -> oauth_consumer_key=&oauth_signature_method=PLAINTEXT&\ -> oauth_signature=%26&oauth_timestamp=$(date +%s)&\ -> oauth_nonce=$(uuidgen)&oauth_version=1.0&\ -> oauth_callback=http%3A%2F%2Flocalhost%3A8080%2Fauth%2Fcallback" -> ``` - -**Response** (`application/x-www-form-urlencoded`) - -```text -oauth_token=&oauth_token_secret=&oauth_callback_confirmed=true -``` - -Store the `oauth_token_secret` securely server-side — you will need it in step 3. - -##### Redirect the user to authorize - -Redirect the user's browser to the authorization page with the request token: - -```text -https://api.clever-cloud.com/v2/oauth/authorize?oauth_token= -``` - -The user logs into Clever Cloud (if not already) and is presented with a permissions form. Available permissions are: - -| Permission | Description | -| --------------------------------------------- | -------------------------------------------- | -| `access_organisations` | Access organisations | -| `access_organisations_bills` | Access organisations' bills | -| `access_organisations_consumption_statistics` | Access organisations' consumption statistics | -| `access_organisations_credit_count` | Access organisations' credit count | -| `access_personal_information` | Access personal information | -| `manage_organisations` | Manage organisations | -| `manage_organisations_applications` | Manage organisations' applications | -| `manage_organisations_members` | Manage organisations' members | -| `manage_organisations_services` | Manage organisations' add-ons | -| `manage_personal_information` | Manage personal information | -| `manage_ssh_keys` | Manage SSH keys | - -You can retrieve this list programmatically with `GET https://api.clever-cloud.com/v2/oauth/rights`. - -Once the user approves, the browser is redirected to your `oauth_callback` URL with the following **query string** parameters: - -| Parameter | Description | -| ---------------- | -------------------------------------------------- | -| `oauth_token` | The request token (must match the one from step 1) | -| `oauth_verifier` | Verification code to exchange for an access token | - -##### Exchange for an access token - -Exchange the request token and verifier for an access token. As with step 1, parameters can be sent as query string or form body: - -- `POST https://api.clever-cloud.com/v2/oauth/access_token_query` — parameters as query string -- `POST https://api.clever-cloud.com/v2/oauth/access_token` — parameters as `application/x-www-form-urlencoded` body - -**Required parameters:** - -| Parameter | Description | -| ------------------------ | -------------------------------------------------------------------- | -| `oauth_consumer_key` | Your consumer key | -| `oauth_signature_method` | Same method as step 1 | -| `oauth_signature` | Request signature (signed with the request token secret from step 1) | -| `oauth_timestamp` | Current Unix timestamp (seconds) | -| `oauth_nonce` | Unique random string | -| `oauth_version` | `1.0` | -| `oauth_token` | The request token from step 1 | -| `oauth_verifier` | The verifier from the callback redirect | - -**Response** (`application/x-www-form-urlencoded`) - -```text -oauth_token=&oauth_token_secret=&expiration_date= -``` - -Store both `oauth_token` and `oauth_token_secret` securely — you will need them to sign every subsequent API request. - -> [!NOTE] -> Access tokens expire after **3 months** by default. The response includes an `expiration_date` field (ISO 8601 format). - -{{% /steps %}} - -#### Making authenticated API requests - -Once you have the four credentials (consumer key, consumer secret, user token, user token secret), sign every API request using the `Authorization` header: - -```text -Authorization: OAuth oauth_consumer_key="", oauth_token="", oauth_signature_method="HMAC-SHA512", oauth_signature="", oauth_timestamp="", oauth_nonce="", oauth_version="1.0" -``` - -**Example** — Fetching the authenticated user's profile: - -```javascript -// buildBaseString, sign and authorizationHeader are defined in the "Signing requests" section -const params = { - oauth_consumer_key: CONSUMER_KEY, - oauth_signature_method: "HMAC-SHA512", - oauth_timestamp: Math.floor(Date.now() / 1000).toString(), - oauth_nonce: crypto.randomUUID().replace(/-/g, ""), - oauth_version: "1.0", - oauth_token: ACCESS_TOKEN, -}; - -const baseString = buildBaseString("GET", "https://api.clever-cloud.com/v2/self", params); -params.oauth_signature = sign(baseString, ACCESS_TOKEN_SECRET); - -const res = await fetch("https://api.clever-cloud.com/v2/self", { - headers: { Authorization: authorizationHeader(params) }, -}); -const user = await res.json(); -``` - -#### Signing requests - -Three signature methods are supported. `HMAC-SHA512` is recommended for production use. - -##### PLAINTEXT - -The simplest method. The signature is the consumer secret and token secret, percent-encoded per RFC 3986 and joined with `&`: - -```javascript -const signature = percentEncode(consumerSecret) + "&" + percentEncode(tokenSecret); -``` - -For the request token step (where no token secret exists yet), leave the second part empty: - -```javascript -const signature = percentEncode(consumerSecret) + "&"; -``` - -> [!NOTE] -> The `percentEncode` function is defined in the [HMAC section below](#hmac-sha1sha512). For secrets containing only alphanumeric characters, `encodeURIComponent` produces the same result. - -##### HMAC-SHA1/SHA512 - -These methods sign a **base string** to ensure the request has not been tampered with. The code snippets below are a JavaScript/Node.js implementation example. - -**1.** Define a `percentEncode` helper per RFC 3986: - -```javascript -// Like encodeURIComponent, but also encodes !'()* -function percentEncode(str) { - return encodeURIComponent(str) - .replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase()); -} -``` - -**2.** Define `buildBaseString` — it concatenates the HTTP method, URL, and sorted parameters, all percent-encoded and joined with `&`: - -```javascript -function buildBaseString(method, url, params) { - const sorted = Object.keys(params) - .filter(k => k !== "oauth_signature") - .sort() - .map(k => percentEncode(k) + "=" + percentEncode(params[k])) - .join("&"); - return method.toUpperCase() + "&" + percentEncode(url) + "&" + percentEncode(sorted); -} -``` - -The three components are: - -- The HTTP method in uppercase (`GET`, `POST`, etc.) -- The base URL (without query string), percent-encoded -- All request parameters (OAuth parameters excluding `oauth_signature`, plus any query string or form body parameters), sorted alphabetically by key — then by value in case of duplicates — formatted as `key=value` pairs joined by `&`, then percent-encoded as a single string - -**3.** Define `sign` — it computes an HMAC with the chosen algorithm and returns the base64-encoded result: - -```javascript -const { createHmac } = await import("node:crypto"); - -function sign(baseString, tokenSecret = "") { - const signingKey = percentEncode(consumerSecret) + "&" + percentEncode(tokenSecret); - return createHmac("sha512", signingKey).update(baseString).digest("base64"); -} -``` - -**4. Build the Authorization header** with all OAuth parameters (including the signature): - -```javascript -function authorizationHeader(params) { - const pairs = Object.entries(params) - .map(([k, v]) => percentEncode(k) + '="' + percentEncode(v) + '"') - .join(", "); - return "OAuth " + pairs; -} -``` diff --git a/content/changelog/2025/03-07-rust-sdk-0.13.md b/content/changelog/2025/03-07-rust-sdk-0.13.md index 2e34b64ff..df46a1e67 100644 --- a/content/changelog/2025/03-07-rust-sdk-0.13.md +++ b/content/changelog/2025/03-07-rust-sdk-0.13.md @@ -15,4 +15,4 @@ description: New Auth solutions for your Rust applications excludeSearch: true --- -[Rust SDK v0.13](https://github.com/CleverCloud/clevercloud-sdk-rust/releases/tag/v0.13.1) is available, uses Rust 1.85 and can now use [API tokens](/api/howto/) or get credentials from [Clever Tools](https://github.com/CleverCloud/clever-tools/) configuration file if present in the system. +[Rust SDK v0.13](https://github.com/CleverCloud/clevercloud-sdk-rust/releases/tag/v0.13.1) is available, uses Rust 1.85 and can now use [API tokens](/api/) or get credentials from [Clever Tools](https://github.com/CleverCloud/clever-tools/) configuration file if present in the system. diff --git a/content/changelog/2025/05-28-oauth-consumer-new-interface.md b/content/changelog/2025/05-28-oauth-consumer-new-interface.md index 74045e3f5..d396db259 100644 --- a/content/changelog/2025/05-28-oauth-consumer-new-interface.md +++ b/content/changelog/2025/05-28-oauth-consumer-new-interface.md @@ -17,6 +17,6 @@ excludeSearch: true A new interface is available [in Clever Cloud Console to create](https://console.clever-cloud.com/users/me/oauth-consumers/new) and manage OAuth consumers. It's better, clearer, simpler, and uses our open source [web components](https://clever.cloud/developers/clever-components/). -- [Learn more about OAuth consumers](/api/howto/#oauth1) +- [Learn more about OAuth consumers](/api/#oauth1) ![New OAuth consumers interface](/images/new-oauth-consumer.webp) diff --git a/content/changelog/2025/06-05-api-tokens-console-tips.md b/content/changelog/2025/06-05-api-tokens-console-tips.md index 6bc6d22c6..f98a7ccd0 100644 --- a/content/changelog/2025/06-05-api-tokens-console-tips.md +++ b/content/changelog/2025/06-05-api-tokens-console-tips.md @@ -15,7 +15,7 @@ description: API tokens and tips everywhere! excludeSearch: true --- -With [Clever Tools 3.12](/changelog/2025/03-07-clever-tools-3.12/), we've introduced a new way to query Clever Cloud's API, with [the API bridge and API tokens](/api/howto/#api-tokens). Starting today, you can create, edit and revoke API tokens directly [from your Clever Cloud profile page](https://console.clever-cloud.com/users/me/api-tokens). +With [Clever Tools 3.12](/changelog/2025/03-07-clever-tools-3.12/), we've introduced a new way to query Clever Cloud's API, with [the API bridge and API tokens](/api/#api-tokens). Starting today, you can create, edit and revoke API tokens directly [from your Clever Cloud profile page](https://console.clever-cloud.com/users/me/api-tokens). ## API tokens in the Clever Cloud Console diff --git a/content/changelog/2025/06-05-oauth-tokens-new-interface.md b/content/changelog/2025/06-05-oauth-tokens-new-interface.md index fc3db4d7e..736fb6f20 100644 --- a/content/changelog/2025/06-05-oauth-tokens-new-interface.md +++ b/content/changelog/2025/06-05-oauth-tokens-new-interface.md @@ -19,6 +19,6 @@ After [renewing OAuth consumers management](/changelog/2025/05-28-oauth-consumer We also added a link to Clever Cloud's OAuth tokens documentation. -- [Learn more about OAuth tokens](/api/howto/#oauth1) +- [Learn more about OAuth tokens](/api/#oauth1) ![New OAuth tokens interface](/images/console-new-oauth-tokens.webp) diff --git a/content/changelog/2026/04-09-clever-tools-4.8.md b/content/changelog/2026/04-09-clever-tools-4.8.md index e29d9a55a..723ad8414 100644 --- a/content/changelog/2026/04-09-clever-tools-4.8.md +++ b/content/changelog/2026/04-09-clever-tools-4.8.md @@ -19,7 +19,7 @@ excludeSearch: true ## OAuth consumers management -You can now create and manage [OAuth consumers](/api/howto/#create-an-oauth-consumer) directly from the CLI. The new `clever oauth-consumers` command set covers the full lifecycle: list, create, get details, update, open in the Console and delete. Consumers can be referenced by name (when unambiguous) or by their consumer key, and the `get` command supports a `--with-secret` flag when you need to retrieve the consumer secret for application configuration. +You can now create and manage [OAuth consumers](/api/#create-an-oauth-consumer) directly from the CLI. The new `clever oauth-consumers` command set covers the full lifecycle: list, create, get details, update, open in the Console and delete. Consumers can be referenced by name (when unambiguous) or by their consumer key, and the `get` command supports a `--with-secret` flag when you need to retrieve the consumer secret for application configuration. ```bash # List OAuth consumers @@ -47,7 +47,7 @@ clever oauth-consumers delete my-app --yes These commands target the current profile's organisation by default. The `create` command accepts the `--org` option to specify the consumer's organisation. For other commands, the consumer key is enough to identify the resource. -- [Learn more about OAuth consumers](/api/howto/#create-an-oauth-consumer) +- [Learn more about OAuth consumers](/api/#create-an-oauth-consumer) ## Drains on add-ons diff --git a/content/doc/kubernetes/operator.md b/content/doc/kubernetes/operator.md index da1a35460..2524b0df7 100644 --- a/content/doc/kubernetes/operator.md +++ b/content/doc/kubernetes/operator.md @@ -82,7 +82,7 @@ Before you begin, ensure that you have the following tools and resources based o - **Kubernetes Cluster:** Ensure you have access to a running Kubernetes cluster. - **Kubectl:** Install Kubernetes command-line tool for managing cluster resources Installation guide available at . -- **Clever Cloud Account Credentials:** [Obtain API tokens and secrets from your Clever Cloud](/api/howto) account to configure the operator. +- **Clever Cloud Account Credentials:** [Obtain API tokens and secrets from your Clever Cloud](/api/) account to configure the operator. These prerequisites are essential for getting started with the Clever Kubernetes Operator, whether you're contributing to its development or deploying it in production. @@ -99,7 +99,7 @@ This is the most complete authentication method, requiring all four OAuth1 param - _Token_ - _Secret_ -To obtain these credentials, you need to connect to the Clever Cloud API, which uses OAuth1-based authentication. As explained in the [Clever Cloud API Overview](/api/howto), you need to create an OAuth consumer token in the Clever Cloud console, use it to obtain the _Consumer key_ and the _Consumer Secret_, and complete the OAuth authentication process to get the _Token_ and _Secret_. +To obtain these credentials, you need to connect to the Clever Cloud API, which uses OAuth1-based authentication. As explained in the [Clever Cloud API Overview](/api/), you need to create an OAuth consumer token in the Clever Cloud console, use it to obtain the _Consumer key_ and the _Consumer Secret_, and complete the OAuth authentication process to get the _Token_ and _Secret_. Configuration example: diff --git a/hugo.yaml b/hugo.yaml index 7ab573b17..2f93ae061 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -48,7 +48,7 @@ menu: weight: 3 - name: "How to use" parent: api - url: "/api/howto" + url: "/api/" - name: "v2 endpoints" parent: api url: "/api/v2" From 7901b306de81c965a42815f373f1083b643ecbd2 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 09:13:40 +0200 Subject: [PATCH 156/214] find-help: merge support into the index and restructure the FAQ --- content/doc/find-help/_index.md | 40 ++++- content/doc/find-help/faq.md | 219 ++++++++++++++--------- content/doc/find-help/support.md | 42 ----- content/doc/find-help/troubleshooting.md | 4 +- shared/ask-for-help.md | 1 + shared/more-config.md | 2 +- 6 files changed, 176 insertions(+), 132 deletions(-) delete mode 100644 content/doc/find-help/support.md create mode 100644 shared/ask-for-help.md diff --git a/content/doc/find-help/_index.md b/content/doc/find-help/_index.md index d122b8fd6..20850a529 100644 --- a/content/doc/find-help/_index.md +++ b/content/doc/find-help/_index.md @@ -11,12 +11,46 @@ keywords: - troubleshooting - resources - assistance +- technical assistance +- email support +- ticket center aliases: - /doc/administrate/find-help +- /doc/find-help/support +- /doc/get-help/support +- /doc/support +- /support --- +The base offer includes support at no extra cost, operated by Clever Cloud's own teams in France, even those who are not paying customers. + +## Ticket Center + +You can find in the console a button to start a conversation with our engineers. For each new ticket it will create a new thread, on a specific subject. Therefore, you can create several tickets for each issue. + +If you open a ticket inside an organisation with members, they will be added to the discussion by email. + +Feel free to use it when this service is available, from 9 a.m to 6 p.m CEST+2 during open business days. + +- [Access Clever Cloud Ticket Center](https://console.clever-cloud.com/ticket-center-choice) + +## Email + +Seek help by querying us by email, allowing you to quickly gain answers to your Clever Cloud specific questions and open a ticket directly with our support staff. We will respond to your email within a few hours, or two business days in the worst case. + +- + +## Critical application support + +Clever Cloud offers a premium support option providing 24/7 a direct line to our team of support engineers. Our team’s focus is investigating and solving new problems, getting feedback on the service and working hard to improve the core service. + +Get more information and contact our sales team at: + +- + +## Learn more + {{< cards >}} - {{< card link="/developers/doc/find-help/faq" title="FAQ" icon="question-mark-circle" >}} - {{< card link="/developers/doc/find-help/support" title="Support" icon="lifebuoy" >}} - {{< card link="/developers/doc/find-help/troubleshooting" title="Troubleshooting" icon="magnifying-glass" >}} + {{< card link="/developers/doc/find-help/faq" title="FAQ" subtitle="Find answers to the most common questions" icon="question-mark-circle" >}} + {{< card link="/developers/doc/find-help/troubleshooting" title="Troubleshooting" subtitle="Diagnose and fix common deployment issues" icon="magnifying-glass" >}} {{< /cards >}} diff --git a/content/doc/find-help/faq.md b/content/doc/find-help/faq.md index 2a6a19594..86676b697 100644 --- a/content/doc/find-help/faq.md +++ b/content/doc/find-help/faq.md @@ -14,7 +14,15 @@ aliases: - /doc/get-help/faq --- -## What is a Scaler? +{{< hextra/hero-subtitle style="margin:.3rem 0 2rem 0">}} + Answers to the questions asked most often about the platform, from scalers and supported runtimes to domains, security, add-ons and backups. If you're facing a specific error message, start with the [troubleshooting page](/doc/find-help/troubleshooting). +{{< /hextra/hero-subtitle >}} + +{{% content "ask-for-help" %}} + +## Platform and plans + +{{% details title="What is a Scaler?" closed="true" %}} A _scaler_ is an individual instance hosting your application. You can allocate one or more scalers to each application. Available flavors are `pico`, `nano`, `XS`, `S`, `M`, `L`, `XL`, `2XL` and `3XL`, depending on the runtime. @@ -25,8 +33,9 @@ When enabling auto-scalability, you have to set a minimum and a maximum of activ {{< callout type="warning" >}} Nano and pico instances operate with **reduced CPU priority** on the host system. As a result, during periods of high load on the hypervisor, these instances may experience performance degradation (since they yield processing power to higher-priority workloads). {{< /callout >}} +{{% /details %}} -## Which application runtimes are supported by Clever Cloud? +{{% details title="Which application runtimes are supported by Clever Cloud?" closed="true" %}} Currently, Clever Cloud supports: @@ -48,89 +57,55 @@ Currently, Clever Cloud supports: - V (Vlang) See the full list in the [applications documentation](/doc/applications/), or browse the [guides](/guides/) for frameworks and services you can deploy on Clever Cloud. +{{% /details %}} -## How many applications can I create? +{{% details title="How many applications can I create?" closed="true" %}} As many as you want. We've not set a limited number of apps by developer. +{{% /details %}} -## How to setup domain names I own? - -You can bind custom domain names to your applications. Please have a look at [Custom Domain Names](/doc/administrate/domain-names). - -## How can I disable one of my existing applications? - -Log in with your account to [console.clever-cloud.com](https://console.clever-cloud.com), and select the appropriate organisation and app in the left column. Then click on the application name and select **Overview**. Click on the **Stop** button to stop your app. - -## What type of content can I deploy? +{{% details title="What type of content can I deploy?" closed="true" %}} Please refer to the Acceptable Use Policy, article 2, [_Reasonable use of the Platform_](https://www.clever.cloud/acceptable-use-policy/). +{{% /details %}} -## How do I add or remove members in my organisations? - -Log in with your account to [console.clever-cloud.com](https://console.clever-cloud.com), and select the appropriate organisation in the left panel. Then click on **Members** in the mid pane. You'll see a list of the organisation's members. If your are an admin, you can revoke or grant permissions. - -## How do I report an application that infringes your Terms and Conditions? +{{% details title="Where are my applications and add-ons located?" closed="true" %}} -To report an application that infringes Clever Cloud's Terms and Conditions, please contact the legal team at . +Applications and add-ons are deployed in multiple regions across Europe, North America and Asia-Pacific, including Paris, Roubaix, Gravelines, London, Warsaw, Montreal, Singapore and Sydney, plus HDS-certified zones in France for healthcare data. When creating an application or an add-on, you can choose among the deployment zones available for that product. -We will investigate and contact the application's owner over the violation if needed. +Clever Cloud is based in Nantes, France. +{{% /details %}} -## Does Clever Cloud support TLS/SSL (HTTPS)? +{{% details title="I want to run Kubernetes on top of Clever Cloud, is that possible?" closed="true" %}} -Yes. For testing purposes, `cleverapps.io` domains support TLS out of the box. For custom SSL certificates, you can either generate one automatically with Let's Encrypt while adding a domain, or [use an existing one](/doc/administrate/ssl/#uploading-my-own-certificates). -Have a look at [installing TLS certificates](/doc/administrate/ssl), and feel free to contact the support team in the [Ticket Center](https://console.clever-cloud.com/ticket-center-choice) if you have questions. +Yes. [Clever Kubernetes Engine](/doc/kubernetes/) (CKE), currently in public beta, provides a managed Kubernetes control plane. See the [product page](https://www.clever.cloud/product/kubernetes/) for more information. +{{% /details %}} -## What are the supported ciphers ? +{{% details title="I want to use Clever Cloud on my own premises, is that possible?" closed="true" %}} -As this information can change over time with security updates, here's the nmap command to look up SSL/TLS ciphers on a Clever Cloud configured domain: +Yes, since 2016 Clever Cloud is packaged for private data center. This offer called "Clever Cloud On Premises" is available upon request: you can send a mail to [sales@clever-cloud.com](mailto:sales@clever-cloud.com) or visit [https://www.clever.cloud/on-premises](https://www.clever.cloud/on-premises) for more info. +{{% /details %}} -```shell -nmap --script ssl-enum-ciphers -p 443 example.com -``` +## Applications and deployments -## I'd like to have two applications available on the same domain name +{{% details title="How can I disable one of my existing applications?" closed="true" %}} -Refer to [prefix routing](/doc/administrate/domain-names/#prefix-routing) to learn how to have two applications share a domain name. +Log in with your account to [console.clever-cloud.com](https://console.clever-cloud.com), and select the appropriate organisation and app in the left column. Then click on the application name and select **Overview**. Click on the **Stop** button to stop your app. +{{% /details %}} -## How do I define cron jobs for my application? +{{% details title="How do I define cron jobs for my application?" closed="true" %}} See [Cron Configuration File](/doc/administrate/cron) for more information. +{{% /details %}} -## How to know if a user comes from a secure connection? - -Load-balancers handles all connections ahead of your applications and forward them in plain HTTP, you can't rely on the server port to know the scheme used by the user. - -Instead, you can use the `X-Forwarded-Proto` HTTP header to get the information, it's set to either '_http_' or '_https_'. - -{{< callout type="info" >}} -In order to use `request.secure` instead of using the header, you must add `XForwardedSupport=all` in your `application.conf`. -{{< /callout >}} - -{{< callout type="warning" >}} -In order to use `request.secure` instead of accessing the header, you must add `trustxforwarded=true` in your `application.conf`. -{{< /callout >}} - -## PHP: `$_SERVER` auth variables are always empty, how do I make this work? - -- [Learn more about the $_SERVER variable on Clever Cloud](/doc/applications/php/apache/#using-http-authentication) - -## How to get the user's IP address? - -Load-balancers ahead of your applications handle all connections and forward them in plain HTTP. -So if you get the `REMOTE_ADDR` or `Client-IP` header, you get only the IP of the load balancer that forwarded the user request. - -To get the original client's IP address, use the `X-Forwarded-For` HTTP header. The load balancer automatically adds this header to each request. - -The `X-Forwarded-For` header contains a comma-separated list of IP addresses. The first address in this list is your end user's original IP address. Any subsequent addresses represent the proxies that the request passed through before reaching your application. -[Read the Wikipedia page for more details](https://en.wikipedia.org/wiki/X-Forwarded-For). - -## How do I identify different instances of my application? +{{% details title="How do I identify different instances of my application?" closed="true" %}} If your application needs to differentiate all the running nodes internally, you can use the `INSTANCE_NUMBER` environment variable. For example, if 3 instances are running for your application, this environment variable will contain `0` on the first, `1` on the second and `2` on the third. +{{% /details %}} -## I need a private ssh key to fetch my private dependencies. How do I do that? +{{% details title="I need a private ssh key to fetch my private dependencies. How do I do that?" closed="true" %}} If your company manages its own artifacts in a private repository (like, you can only access them via git+ssh or sFTP), and you need a private key to connect to the server, you @@ -138,8 +113,9 @@ can commit them in your application's Clever Cloud repository and then add a `clevercloud/ssh.json` file. - [Learn more about ssh.json](/doc/reference/common-configuration/#private-ssh-key) +{{% /details %}} -## I get a `java.lang.UnsupportedClassVersionError: Unsupported major.minor version` error. How can I fix it? +{{% details title="I get a `java.lang.UnsupportedClassVersionError: Unsupported major.minor version` error. How can I fix it?" closed="true" %}} If you get this error on a Java (or any JVM language) application, it means that your application was compiled with a newer Java version than the one used to run it. @@ -161,43 +137,111 @@ For reference, the table below lists the class file version for each major Java | 17 | 61.0 | | 21 | 65.0 | -## I want SSH access to my server +{{% /details %}} + +{{% details title="I want SSH access to my server" closed="true" %}} Clever Cloud does not give you access to a server or a VPS, it makes your application run. Each instance is started and configured automatically, and can be stopped at any moment. If however, you still need SSH access for debugging purposes, please have a look at [SSH access](/doc/cli/applications/deployment-lifecycle/#ssh), but keep in mind that changes made on an instance are not persistent across deployments. +{{% /details %}} -## I want to use Clever Cloud on my own premises, is that possible? +{{% details title="Can I `scp` something in a VM?" closed="true" %}} -Yes, since 2016 Clever Cloud is packaged for private data center. This offer called "Clever Cloud On Premises" is available upon request: you can send a mail to [sales@clever-cloud.com](mailto:sales@clever-cloud.com) or visit [https://www.clever.cloud/on-premises](https://www.clever.cloud/on-premises) for more info. +You cannot `scp` something to the VM, you can however easily `scp` something from the VM to the outside. +{{% /details %}} -## Where are my applications and add-ons located? +{{% details title="I need to convert something to PDF with `wkhtmltopdf`" closed="true" %}} -Applications and add-ons are deployed in multiple regions across Europe, North America and Asia-Pacific, including Paris, Roubaix, Gravelines, London, Warsaw, Montreal, Singapore and Sydney, plus HDS-certified zones in France for healthcare data. When creating an application or an add-on, you can choose among the deployment zones available for that product. +`wkhtmltopdf` is available and fully functional but we deeply recommend to use `chromium headless` instead. +{{% /details %}} -Clever Cloud is based in Nantes, France. +{{% details title="What is the timezone used by my application/add-on?" closed="true" %}} -## I want to run Kubernetes on top of Clever Cloud, is that possible? +All instances on Clever Cloud run on the UTC timezone. We recommend to handle all your dates in UTC internally, and only handle timezones when reading or displaying dates. +{{% /details %}} -Yes. [Clever Kubernetes Engine](/doc/kubernetes/) (CKE), currently in public beta, provides a managed Kubernetes control plane. See the [product page](https://www.clever.cloud/product/kubernetes/) for more information. +## Domains, network and security -## How to setup a firewall on Clever Cloud? +{{% details title="How to setup domain names I own?" closed="true" %}} + +You can bind custom domain names to your applications. Please have a look at [Custom Domain Names](/doc/administrate/domain-names). +{{% /details %}} + +{{% details title="I'd like to have two applications available on the same domain name" closed="true" %}} + +Refer to [prefix routing](/doc/administrate/domain-names/#prefix-routing) to learn how to have two applications share a domain name. +{{% /details %}} + +{{% details title="Does Clever Cloud support TLS/SSL (HTTPS)?" closed="true" %}} + +Yes. For testing purposes, `cleverapps.io` domains support TLS out of the box. For custom SSL certificates, you can either generate one automatically with Let's Encrypt while adding a domain, or [use an existing one](/doc/administrate/ssl/#uploading-my-own-certificates). +Have a look at [installing TLS certificates](/doc/administrate/ssl), and feel free to contact the support team in the [Ticket Center](https://console.clever-cloud.com/ticket-center-choice) if you have questions. +{{% /details %}} + +{{% details title="What are the supported ciphers?" closed="true" %}} + +As this information can change over time with security updates, here's the nmap command to look up SSL/TLS ciphers on a Clever Cloud configured domain: + +```shell +nmap --script ssl-enum-ciphers -p 443 example.com +``` + +{{% /details %}} + +{{% details title="How to know if a user comes from a secure connection?" closed="true" %}} + +Load-balancers handles all connections ahead of your applications and forward them in plain HTTP, you can't rely on the server port to know the scheme used by the user. + +Instead, you can use the `X-Forwarded-Proto` HTTP header to get the information, it's set to either '_http_' or '_https_'. + +{{< callout type="info" >}} +In order to use `request.secure` instead of using the header, you must add `XForwardedSupport=all` in your `application.conf`. +{{< /callout >}} + +{{< callout type="warning" >}} +In order to use `request.secure` instead of accessing the header, you must add `trustxforwarded=true` in your `application.conf`. +{{< /callout >}} +{{% /details %}} + +{{% details title="PHP: `$_SERVER` auth variables are always empty, how do I make this work?" closed="true" %}} + +- [Learn more about the $_SERVER variable on Clever Cloud](/doc/applications/php/apache/#using-http-authentication) +{{% /details %}} + +{{% details title="How to get the user's IP address?" closed="true" %}} + +Load-balancers ahead of your applications handle all connections and forward them in plain HTTP. +So if you get the `REMOTE_ADDR` or `Client-IP` header, you get only the IP of the load balancer that forwarded the user request. + +To get the original client's IP address, use the `X-Forwarded-For` HTTP header. The load balancer automatically adds this header to each request. + +The `X-Forwarded-For` header contains a comma-separated list of IP addresses. The first address in this list is your end user's original IP address. Any subsequent addresses represent the proxies that the request passed through before reaching your application. +[Read the Wikipedia page for more details](https://en.wikipedia.org/wiki/X-Forwarded-For). +{{% /details %}} + +{{% details title="How to setup a firewall on Clever Cloud?" closed="true" %}} Specific firewall rules can be enabled on demand to the support or in case of attack. +{{% /details %}} -## Can I `scp` something in a VM +{{% details title="I get unknown regular requests, is there a problem?" closed="true" %}} -You cannot `scp` something to the VM, you can however easily `scp` something from the VM to the outside. +The platform performs routine health checks to applications every 2 minutes. You may notice these periodic HTTP requests in your logs, with `X-Clevercloud-Monitoring` header. They're part of Clever Cloud's standard monitoring process. +{{% /details %}} -## I need to convert something to PDF with `wkhtmltopdf` +## Add-ons and data -`wkhtmltopdf` is available and fully functional but we deeply recommend to use `chromium headless` instead. +{{% details title="What is a DEV plan?" closed="true" %}} -## What is the timezone used by my application/add-on? +DEV plan is a free-tier plan available for some databases, designed to let customers explore and test these products. They operate on shared clusters, which may result in variable performance; they also have no backups nor SLA guarantees. -All instances on Clever Cloud run on the UTC timezone. We recommend to handle all your dates in UTC internally, and only handle timezones when reading or displaying dates. +Some features such as extensions, simultaneous connections numbers, functions… might be reduced or unavailable. + +Support is not able to provide help in case of DEV plan. +{{% /details %}} -## I received an email saying "Add-on [my add-on] disk is nearly full". What do I do? +{{% details title="I received an email saying 'Add-on [my add-on] disk is nearly full'. What do I do?" closed="true" %}} A full disk can cause your database to crash or become unresponsive. Consider upgrading your add-on plan. @@ -214,26 +258,31 @@ Then you can remove records from your database, re-index your tables and try to You can buy more disk space by migrating your add-on to a higher plan. If a VACUUM operation needs more disk that there is remaining, migrating to the same plan cleans up the file on disk and regains space. +{{% /details %}} -## Where are the backups stored? +{{% details title="Where are the backups stored?" closed="true" %}} Clever Cloud stores all backups on [Cellar](https://www.clever.cloud/product/cellar-object-storage/), a replicated object storage service with three copies distributed across datacenters in the PAR region to ensure durability. Even if one datacenter fails, your backups remain safe. For custom configurations (for example, multiple retention policies), contact Support. To locate backups not visible in the Console, use [Clever Tools](https://github.com/CleverCloud/clever-tools) with: `clever database backups DATABASE-ID [--format, -F] FORMAT`. Find more [documentation on restoring backups with the CLI](/doc/cli/addons/#database-backups). +{{% /details %}} -## I can't create my add-on +{{% details title="I can't create my add-on" closed="true" %}} To create add-ons, you need to complete your account information, including your city and ZIP code. For instance, you cannot create a Matomo add-on until you provide these details. +{{% /details %}} -## I get unknown regular requests, is there a problem ? +## Account and organisations -The platform performs routine health checks to applications every 2 minutes. You may notice these periodic HTTP requests in your logs, with `X-Clevercloud-Monitoring` header. They're part of Clever Cloud's standard monitoring process. +{{% details title="How do I add or remove members in my organisations?" closed="true" %}} -## What is a DEV plan ? +Log in with your account to [console.clever-cloud.com](https://console.clever-cloud.com), and select the appropriate organisation in the left panel. Then click on **Members** in the mid pane. You'll see a list of the organisation's members. If your are an admin, you can revoke or grant permissions. +{{% /details %}} -DEV plan is a free-tier plan available for some databases, designed to let customers explore and test these products. They operate on shared clusters, which may result in variable performance; they also have no backups nor SLA guarantees. +{{% details title="How do I report an application that infringes your Terms and Conditions?" closed="true" %}} -Some features such as extensions, simultaneous connections numbers, functions… might be reduced or unavailable. +To report an application that infringes Clever Cloud's Terms and Conditions, please contact the legal team at . -Support is not able to provide help in case of DEV plan. +We will investigate and contact the application's owner over the violation if needed. +{{% /details %}} diff --git a/content/doc/find-help/support.md b/content/doc/find-help/support.md deleted file mode 100644 index 54409b27d..000000000 --- a/content/doc/find-help/support.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -type: docs -weight: 7 -linkTitle: Support -title: Support -description: Learn how to contact Clever Cloud support through email or ticket center for technical assistance, available during business hours -keywords: -- support -- technical assistance -- email support -- ticket center -- clever cloud help -- customer service -aliases: -- /doc/get-help/support -- /doc/support -- /support ---- - -We provide basic support free-of-charge to all our users, even those who are not paying customers. - -## Email - -Seek help by querying us by email, allowing you to quickly gain answers to your Clever Cloud specific questions and open a ticket directly with our support staff. We will respond to your email within a few hours, or two business days in the worst case. - -- - -## Ticket Center - -You can find in the console a button to [start a conversation](https://console.clever-cloud.com/ticket-center-choice) with our engineers. For each new ticket it will create a new thread, on a specific subject. Therefore, you can create several tickets for each issue. - -If you open a ticket inside an organisation with members, they will be added to the discussion by e-mail. - -Feel free to use it when this service available, from 9 a.m to 6 p.m CEST+2 during open business days. - -## Critical application support - -Clever Cloud offers a premium support option providing 24/7 a direct line to our team of support engineers. Our team’s focus is investigating and solving new problems, getting feedback on the service and working hard to improve the core service. - -Get more information and contact our sales team at: - -- diff --git a/content/doc/find-help/troubleshooting.md b/content/doc/find-help/troubleshooting.md index 7dc8019d5..78f375024 100644 --- a/content/doc/find-help/troubleshooting.md +++ b/content/doc/find-help/troubleshooting.md @@ -15,9 +15,11 @@ aliases: --- {{< hextra/hero-subtitle style="margin:.3rem 0 2rem 0">}} - If you're having trouble deploying applications or with with git, this section is for you. From git issues to configuration errors, problems can arise at different stages of your deployments. + If you're having trouble deploying applications or with git, this section is for you. From git issues to configuration errors, problems can arise at different stages of your deployments. For general questions about the platform, see the [FAQ](/doc/find-help/faq). {{< /hextra/hero-subtitle >}} +{{% content "ask-for-help" %}} + ## Git-related issues {{% details title="Some files disappear after an application restart" closed="true" %}} diff --git a/shared/ask-for-help.md b/shared/ask-for-help.md new file mode 100644 index 000000000..50224262d --- /dev/null +++ b/shared/ask-for-help.md @@ -0,0 +1 @@ +Can't find what you're looking for? Contact the [support team](/doc/find-help/), or [open an issue](https://github.com/CleverCloud/documentation/issues) to suggest an addition to this page. diff --git a/shared/more-config.md b/shared/more-config.md index e1fa63df6..7143ba58d 100644 --- a/shared/more-config.md +++ b/shared/more-config.md @@ -6,4 +6,4 @@ Go check the [Common configuration page](/doc/reference/common-configuration). You may want to have an advanced usage of your application, in which case we recommend you to read the **Administrate** documentation section. -If you can't find something or have a specific need like using a non supported version of a particular software, please reach out to the [support](/doc/find-help/support). +If you can't find something or have a specific need like using a non supported version of a particular software, please reach out to the [support](/doc/find-help/). From 9b26b8e1bbb4114b46a55537dedeab949bbdf8eb Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 09:13:59 +0200 Subject: [PATCH 157/214] doc: reorder the sidebar and give each page a distinct weight --- content/doc/account/_index.md | 2 +- content/doc/addons/_index.md | 2 +- content/doc/administrate/_index.md | 2 +- content/doc/applications/_index.md | 2 +- content/doc/best-practices/_index.md | 2 +- content/doc/billing/_index.md | 2 +- content/doc/ci-cd/_index.md | 2 +- content/doc/cli/_index.md | 2 +- content/doc/contribute.md | 2 +- content/doc/develop/_index.md | 2 +- content/doc/find-help/_index.md | 2 +- content/doc/functions/_index.md | 2 +- content/doc/kubernetes/_index.md | 2 +- content/doc/marketplace.md | 2 +- content/doc/metrics/_index.md | 2 +- content/doc/reference/_index.md | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/content/doc/account/_index.md b/content/doc/account/_index.md index 46a66dad8..f5082dfd1 100644 --- a/content/doc/account/_index.md +++ b/content/doc/account/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 11 +weight: 14 linkTitle: User Account title: User Account description: Learn how to manage your account on Clever Cloud. Discover detailed instructions for account creation, organisation administration in our PaaS platform. diff --git a/content/doc/addons/_index.md b/content/doc/addons/_index.md index 901834b8e..ac5505aae 100644 --- a/content/doc/addons/_index.md +++ b/content/doc/addons/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 3 +weight: 4 linkTitle: Add-ons title: Add-ons description: Discover and configure add-ons on Clever Cloud including databases, storage services, and managed tools for your applications diff --git a/content/doc/administrate/_index.md b/content/doc/administrate/_index.md index be3531369..c1ea9820a 100644 --- a/content/doc/administrate/_index.md +++ b/content/doc/administrate/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 5 +weight: 8 linkTitle: Administrate title: Administrate description: Manage and administrate your Clever Cloud applications including scaling, monitoring, SSL configuration, and platform operations diff --git a/content/doc/applications/_index.md b/content/doc/applications/_index.md index 26f5c42b9..1fbda2588 100644 --- a/content/doc/applications/_index.md +++ b/content/doc/applications/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 2 +weight: 3 linkTitle: Applications title: Applications description: Deploy applications on Clever Cloud with support for popular languages, frameworks, and deployment configurations across modern runtimes diff --git a/content/doc/best-practices/_index.md b/content/doc/best-practices/_index.md index c4955a8a9..17d3fbcad 100644 --- a/content/doc/best-practices/_index.md +++ b/content/doc/best-practices/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 9 +weight: 11 linkTitle: Best practices title: Best practices description: Learn best practices for deploying and managing applications on Clever Cloud including security, performance, and scalability tips diff --git a/content/doc/billing/_index.md b/content/doc/billing/_index.md index 2b886776a..fb1d5bd03 100644 --- a/content/doc/billing/_index.md +++ b/content/doc/billing/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 12 +weight: 15 linkTitle: Billing title: Billing description: Manage billing and payment information for your Clever Cloud account including invoices, consumption tracking, and pricing details diff --git a/content/doc/ci-cd/_index.md b/content/doc/ci-cd/_index.md index 5883fe8b8..8c2f21c0c 100644 --- a/content/doc/ci-cd/_index.md +++ b/content/doc/ci-cd/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 9 +weight: 12 linkTitle: CI/CD title: CI/CD description: Deploy to Clever Cloud from GitLab or GitHub with complete setup and configuration, configuration options, and deployment best practices diff --git a/content/doc/cli/_index.md b/content/doc/cli/_index.md index c97f6ee39..9fabcb610 100644 --- a/content/doc/cli/_index.md +++ b/content/doc/cli/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 8 +weight: 10 linkTitle: Clever Tools (CLI) title: Clever Tools (CLI) description: Use Clever Tools CLI to manage applications, add-ons, and deployments from the command line with powerful automation and monitoring features diff --git a/content/doc/contribute.md b/content/doc/contribute.md index 555734b50..0def99fef 100644 --- a/content/doc/contribute.md +++ b/content/doc/contribute.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Contribute -weight: 14 +weight: 17 title: Contribute description: Learn how to contribute to Clever Cloud documentation including available shortcodes, writing guidelines, and submission process keywords: diff --git a/content/doc/develop/_index.md b/content/doc/develop/_index.md index c59fd4718..f79c3f468 100644 --- a/content/doc/develop/_index.md +++ b/content/doc/develop/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 6 +weight: 9 linkTitle: Develop title: Develop description: Development guides and best practices for building applications on Clever Cloud including environment variables, build hooks, and deployment strategies diff --git a/content/doc/find-help/_index.md b/content/doc/find-help/_index.md index 20850a529..5f4988d43 100644 --- a/content/doc/find-help/_index.md +++ b/content/doc/find-help/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 13 +weight: 2 linkTitle: Find Help title: Find Help description: Access support resources including professional support options, FAQ, and troubleshooting guides for Clever Cloud platform issues diff --git a/content/doc/functions/_index.md b/content/doc/functions/_index.md index af7f9a471..13f1e73e7 100644 --- a/content/doc/functions/_index.md +++ b/content/doc/functions/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 3 +weight: 5 linkTitle: Functions title: Functions (FaaS) description: Deploy serverless functions using any language that compiles to WebAssembly for automatic scaling and event-driven execution diff --git a/content/doc/kubernetes/_index.md b/content/doc/kubernetes/_index.md index 3520f359b..9b7341a5d 100644 --- a/content/doc/kubernetes/_index.md +++ b/content/doc/kubernetes/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 4 +weight: 6 linkTitle: Kubernetes title: Clever Kubernetes Engine (CKE) description: Create and manage Kubernetes clusters on Clever Cloud, with managed control plane, Materia etcd and vanilla Kubernetes experience diff --git a/content/doc/marketplace.md b/content/doc/marketplace.md index 65f701e7b..5cd8db040 100644 --- a/content/doc/marketplace.md +++ b/content/doc/marketplace.md @@ -1,7 +1,7 @@ --- type: docs linkTitle: Marketplace APIs & Tools -weight: 13 +weight: 16 title: Marketplace APIs & Tools description: Integrate your own service as an add-on on Clever Cloud marketplace with comprehensive developer tools and partnership opportunities keywords: diff --git a/content/doc/metrics/_index.md b/content/doc/metrics/_index.md index 8b13fcfec..2fe4f3914 100644 --- a/content/doc/metrics/_index.md +++ b/content/doc/metrics/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 4 +weight: 7 linkTitle: Metrics title: Metrics description: Learn to monitor application performance and resource usage on Clever Cloud with built-in metrics, dashboards, alerting, and analytics diff --git a/content/doc/reference/_index.md b/content/doc/reference/_index.md index 92ba39323..415af41df 100644 --- a/content/doc/reference/_index.md +++ b/content/doc/reference/_index.md @@ -1,6 +1,6 @@ --- type: docs -weight: 10 +weight: 13 linkTitle: Reference title: Reference description: Explore technical reference documentation for Clever Cloud including environment variables, API endpoints, and configuration options From cfd81e3493e5c5f19c61ed2c62a0c9f984533537 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 09:14:06 +0200 Subject: [PATCH 158/214] doc(getting-started): rename the quickstart page and update its labels --- content/_index.md | 2 +- content/doc/_index.md | 2 +- content/doc/account/ssh-keys-management.md | 2 +- content/doc/applications/frankenphp.md | 2 +- content/doc/applications/linux.md | 2 +- content/doc/applications/nodejs.md | 2 +- content/doc/applications/php/_index.md | 2 +- content/doc/applications/python/_index.md | 2 +- content/doc/applications/static.md | 2 +- content/doc/applications/v.md | 2 +- content/doc/best-practices/_index.md | 1 - content/doc/billing/_index.md | 1 - content/doc/{quickstart.md => getting-started.md} | 6 +++--- content/guides/hugo-static-s3.md | 4 ++-- content/guides/tutorial-laravel.md | 2 +- shared/create-application.md | 2 +- shared/deploy-ftp.md | 2 +- shared/deploy-git.md | 2 +- 18 files changed, 19 insertions(+), 21 deletions(-) rename content/doc/{quickstart.md => getting-started.md} (99%) diff --git a/content/_index.md b/content/_index.md index dae236a18..9ff1d24d7 100644 --- a/content/_index.md +++ b/content/_index.md @@ -29,7 +29,7 @@ aliases:

diff --git a/content/doc/_index.md b/content/doc/_index.md index 2bfb82022..713fa69b6 100644 --- a/content/doc/_index.md +++ b/content/doc/_index.md @@ -29,7 +29,7 @@ aliases: {{< /hextra/hero-subtitle >}} {{< cards >}} - {{< card link="/developers/doc/quickstart" title="Getting started" subtitle="Deploy an application in five minutes" icon="arrow-circle-right" >}} + {{< card link="/developers/doc/getting-started" title="Getting started" subtitle="Deploy an application in five minutes" icon="arrow-circle-right" >}} {{< card link="/developers/doc/applications" title="Applications" subtitle="Deploy applications with supported runtimes" icon="rocket-launch" >}} {{< card link="/developers/doc/addons" title="Add-ons" subtitle="Connect managed databases, storage and services" icon="plug" >}} {{< card link="/developers/doc/functions" title="Functions" subtitle="Deploy serverless functions compiled to WebAssembly" icon="code-bracket" >}} diff --git a/content/doc/account/ssh-keys-management.md b/content/doc/account/ssh-keys-management.md index d8177a0ac..eb55dc3ed 100644 --- a/content/doc/account/ssh-keys-management.md +++ b/content/doc/account/ssh-keys-management.md @@ -135,7 +135,7 @@ Add the key by entering a name and the public SSH key. The key is the entire con Your public SSH key must be associated with only one account. {{< /callout >}} -If you see "*access denied*" or "*password:*" when you [push on Clever Cloud](/doc/quickstart/#choose-how-to-deploy), your SSH keys may be invalid or not available on Clever Cloud. Please check that you SSH key is present and valid in your profile. +If you see "*access denied*" or "*password:*" when you [push on Clever Cloud](/doc/getting-started/#choose-how-to-deploy), your SSH keys may be invalid or not available on Clever Cloud. Please check that you SSH key is present and valid in your profile. ### Through CC API or Clever cURL diff --git a/content/doc/applications/frankenphp.md b/content/doc/applications/frankenphp.md index d98c04e83..14ab9fcbc 100644 --- a/content/doc/applications/frankenphp.md +++ b/content/doc/applications/frankenphp.md @@ -34,7 +34,7 @@ clever create --type frankenphp ``` - [Learn more about Clever Tools](/doc/cli/) -- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) +- [Learn more about Clever Cloud application deployment](/doc/getting-started/#create-an-application-step-by-step) > [!NOTE] FrankenPHP applications can't be deployed on a pico instance, XS is the default instance type diff --git a/content/doc/applications/linux.md b/content/doc/applications/linux.md index 2f2d6a206..b67f23168 100644 --- a/content/doc/applications/linux.md +++ b/content/doc/applications/linux.md @@ -32,7 +32,7 @@ clever create --type linux ``` - [Learn more about Clever Tools](/doc/cli/) -- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) +- [Learn more about Clever Cloud application deployment](/doc/getting-started/#create-an-application-step-by-step) ## Configure your Linux application diff --git a/content/doc/applications/nodejs.md b/content/doc/applications/nodejs.md index 698e08d05..6b9db12aa 100644 --- a/content/doc/applications/nodejs.md +++ b/content/doc/applications/nodejs.md @@ -47,7 +47,7 @@ clever create --type node ``` - [Learn more about Clever Tools](/doc/cli/) -- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) +- [Learn more about Clever Cloud application deployment](/doc/getting-started/#create-an-application-step-by-step) ## Configure your Node.js & Bun application diff --git a/content/doc/applications/php/_index.md b/content/doc/applications/php/_index.md index 10dd56e56..23d0fd00a 100644 --- a/content/doc/applications/php/_index.md +++ b/content/doc/applications/php/_index.md @@ -42,7 +42,7 @@ clever create --type php ``` - [Learn more about Clever Tools](/doc/cli/) -- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) +- [Learn more about Clever Cloud application deployment](/doc/getting-started/#create-an-application-step-by-step) ## Configure your PHP application diff --git a/content/doc/applications/python/_index.md b/content/doc/applications/python/_index.md index a7c2b5d02..42e792272 100644 --- a/content/doc/applications/python/_index.md +++ b/content/doc/applications/python/_index.md @@ -42,7 +42,7 @@ clever create --type python ``` - [Learn more about Clever Tools](/doc/cli/) -- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) +- [Learn more about Clever Cloud application deployment](/doc/getting-started/#create-an-application-step-by-step) ## Configure your Python application diff --git a/content/doc/applications/static.md b/content/doc/applications/static.md index bc98ce195..a63909ca1 100644 --- a/content/doc/applications/static.md +++ b/content/doc/applications/static.md @@ -43,7 +43,7 @@ clever create --type static ``` - [Learn more about Clever Tools](/doc/cli/) -- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) +- [Learn more about Clever Cloud application deployment](/doc/getting-started/#create-an-application-step-by-step) ## Configure your Static application diff --git a/content/doc/applications/v.md b/content/doc/applications/v.md index 0cae3c86d..0a114c955 100644 --- a/content/doc/applications/v.md +++ b/content/doc/applications/v.md @@ -28,7 +28,7 @@ clever create --type v ``` - [Learn more about Clever Tools](/doc/cli/) -- [Learn more about Clever Cloud application deployment](/doc/quickstart/#create-an-application-step-by-step) +- [Learn more about Clever Cloud application deployment](/doc/getting-started/#create-an-application-step-by-step) ## Configure your V (Vlang) application diff --git a/content/doc/best-practices/_index.md b/content/doc/best-practices/_index.md index 17d3fbcad..1a76d84ef 100644 --- a/content/doc/best-practices/_index.md +++ b/content/doc/best-practices/_index.md @@ -12,7 +12,6 @@ keywords: - scalability - applications aliases: -- /doc/quickstart - /doc/develop/best-practices --- diff --git a/content/doc/billing/_index.md b/content/doc/billing/_index.md index fb1d5bd03..8da782544 100644 --- a/content/doc/billing/_index.md +++ b/content/doc/billing/_index.md @@ -17,7 +17,6 @@ aliases: - /doc/administrate/log-management/billing - /doc/billing - /doc/doc/billing -- /doc/quickstart --- {{< cards >}} diff --git a/content/doc/quickstart.md b/content/doc/getting-started.md similarity index 99% rename from content/doc/quickstart.md rename to content/doc/getting-started.md index 0b14c6cb5..66df441c5 100644 --- a/content/doc/quickstart.md +++ b/content/doc/getting-started.md @@ -1,8 +1,8 @@ --- type: docs -linkTitle: Quickstart +linkTitle: Getting started weight: 1 -title: Quickstart +title: Deploy your first application and resources description: Get started with Clever Cloud by creating an account, deploying your first application, and learning essential platform concepts keywords: - quickstart @@ -17,8 +17,8 @@ aliases: - /deploy - /doc/addons/add-an-addon - /doc/clever-cloud-overview/add-application -- /doc/getting-started - /doc/getting-started/quickstart +- /doc/quickstart - /getting-started/authentication - /getting-started/quickstart - /quickstart diff --git a/content/guides/hugo-static-s3.md b/content/guides/hugo-static-s3.md index 772cd5543..98481b7a4 100644 --- a/content/guides/hugo-static-s3.md +++ b/content/guides/hugo-static-s3.md @@ -17,10 +17,10 @@ You can also use Cellar, a S3-compatible object storage. This hosting method sav To do so, you need: -- a [Clever Cloud account](/doc/quickstart/) +- a [Clever Cloud account](/doc/getting-started/) - [Hugo](https://gohugo.io/) on your machine - to have [created your website](https://gohugo.io/getting-started/quick-start/) files with Hugo -- a Cellar add-on; if you have never created an add-on, you can follow [this guide](/doc/quickstart/#create-your-first-add-on) +- a Cellar add-on; if you have never created an add-on, you can follow [this guide](/doc/getting-started/#create-your-first-add-on) ## Bucket creation and management diff --git a/content/guides/tutorial-laravel.md b/content/guides/tutorial-laravel.md index b78f58781..d9692b4fe 100644 --- a/content/guides/tutorial-laravel.md +++ b/content/guides/tutorial-laravel.md @@ -36,7 +36,7 @@ The following sections explain how to set up each instance step by step: ### Create a PHP application -From the Clever Cloud Console, create a new PHP application and choose your deployment method: [git, GitHub or FTP](/doc/quickstart/#choose-how-to-deploy). +From the Clever Cloud Console, create a new PHP application and choose your deployment method: [git, GitHub or FTP](/doc/getting-started/#choose-how-to-deploy). ### Add `DocumentRoot` variable diff --git a/shared/create-application.md b/shared/create-application.md index f64071aaf..eb241e853 100644 --- a/shared/create-application.md +++ b/shared/create-application.md @@ -3,7 +3,7 @@ ### With the web console -Refer to [Quickstart](/doc/quickstart) for more details on application creation via the console. +Refer to [Quickstart](/doc/getting-started) for more details on application creation via the console. ### With the Clever Tools CLI diff --git a/shared/deploy-ftp.md b/shared/deploy-ftp.md index 18b019489..737342c79 100644 --- a/shared/deploy-ftp.md +++ b/shared/deploy-ftp.md @@ -10,4 +10,4 @@ Just follow the instructions of your FTP Software to send code to Clever Cloud. ⚠️ **An FTP application automatically starts once you create the application, even if you don't send any code yet.** -Refer to [Quick Start - FTP deployment](/doc/quickstart#ftp-deployment) for more details. +Refer to [Quick Start - FTP deployment](/doc/getting-started#choose-how-to-deploy) for more details. diff --git a/shared/deploy-git.md b/shared/deploy-git.md index bed2a8ef4..dc5fcda27 100644 --- a/shared/deploy-git.md +++ b/shared/deploy-git.md @@ -16,4 +16,4 @@ 5. Now push your application on Clever Cloud with `git push master` -Refer to [git deployments](/doc/quickstart) for more details. +Refer to [git deployments](/doc/getting-started) for more details. From ccb4e7fcee2e2d68225fd4f721b17373a0e52f98 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 09:14:10 +0200 Subject: [PATCH 159/214] docs(agents): require aliases and link updates when content moves --- AGENTS.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 8ddc0021f..027829c95 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,15 @@ For changelog entries, also include: - `tags` - Array of product tags (lowercase) - `authors` - Array with `name`, `link`, `image` fields +### Moving or Merging Content + +When a page moves to another URL, or when its content is merged into another page: + +- Always add the old URL to the destination page's `aliases`, so Hugo keeps serving a redirect +- Carry over every alias the removed page already declared; they must keep resolving +- Update internal links to target the new URL directly instead of relying on the redirect, especially in `shared/` blocks included by many pages +- Check each redirect in the build output before committing + ### Shared Content System - Include shared content: `{{% content "filename" %}}` From 5ffd27cc17753b41645d7fac477eaf0152d9a37c Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 09:27:09 +0200 Subject: [PATCH 160/214] feat(hugo): shorten the edit page link and reuse the external link arrow --- assets/css/custom.css | 15 +++++++++++++++ i18n/en.yaml | 1 + 2 files changed, 16 insertions(+) create mode 100644 i18n/en.yaml diff --git a/assets/css/custom.css b/assets/css/custom.css index fc936d9c8..679489777 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -698,3 +698,18 @@ html[class~="dark"] .content table:not(.code-block table) tbody tr:nth-child(odd html[class~="dark"] .swagger-ui .responses-wrapper .response-header .response-toggle svg { fill: rgb(209 213 219); } + +/* "Edit this page" link: reuse the same arrow as external links. + The i18n string can only carry text, so the icon is appended here. + A mask keeps it on currentColor, so hover and dark mode follow the link. */ +.hextra-toc a[rel="noreferrer"]::after { + content: ""; + display: inline-block; + width: 1em; + height: 1em; + margin-left: 0.15em; + vertical-align: baseline; + background-color: currentColor; + -webkit-mask: url('data:image/svg+xml;utf8,') no-repeat center / contain; + mask: url('data:image/svg+xml;utf8,') no-repeat center / contain; +} diff --git a/i18n/en.yaml b/i18n/en.yaml new file mode 100644 index 000000000..71deab0c7 --- /dev/null +++ b/i18n/en.yaml @@ -0,0 +1 @@ +editThisPage: "Edit this page" From 0d8b51d830dc967279f9e9b3b549ff770e9c781e Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 09:27:09 +0200 Subject: [PATCH 161/214] fix(hugo): make the navbar span the full page width --- hugo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo.yaml b/hugo.yaml index 2f93ae061..d75c07c02 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -168,7 +168,7 @@ params: dark: images/logo-dark.svg link: /developers width: 175 - width: wide + width: full page: # full (100%), wide (90rem), normal (1280px) From 4e68eb68b753a0465b3add33d5b868358b44cbfd Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 11:01:51 +0200 Subject: [PATCH 162/214] guides: fix external card links broken by angle brackets --- .markdownlint.jsonc | 5 +++++ content/doc/billing/_index.md | 2 +- content/doc/ci-cd/custom-scripts.md | 4 ++-- content/doc/ci-cd/github.md | 2 +- content/doc/ci-cd/gitlab.md | 2 +- content/guides/docs.md | 2 +- content/guides/docusaurus.md | 2 +- content/guides/eleventy.md | 2 +- content/guides/ghost.md | 2 +- content/guides/hexo.md | 2 +- content/guides/hono.md | 2 +- content/guides/hugo.md | 2 +- content/guides/lume-deno.md | 2 +- content/guides/maudit.md | 2 +- content/guides/mdbook.md | 2 +- content/guides/mkdocs.md | 2 +- content/guides/moodle.md | 2 +- content/guides/nuxt.md | 2 +- content/guides/outline.md | 2 +- content/guides/sftpgo.md | 2 +- content/guides/sveltekit.md | 2 +- content/guides/tutorial-laravel.md | 2 +- 22 files changed, 27 insertions(+), 22 deletions(-) diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc index 25a9920da..d19c533f2 100644 --- a/.markdownlint.jsonc +++ b/.markdownlint.jsonc @@ -12,6 +12,11 @@ "cite" ] }, + // MD034/no-bare-urls : shortcode attributes such as {{< card link="https://..." >}} + // are not Markdown, but markdownlint reads them as bare URLs. Wrapping the value in + // <> silences the rule and makes Go reject the URL, rendering href="#ZgotmplZ". + // Keep URLs bare in shortcode attributes. + "MD034": false, "MD041": false, "MD051": false, "MD055": { diff --git a/content/doc/billing/_index.md b/content/doc/billing/_index.md index 8da782544..5e24ee8e5 100644 --- a/content/doc/billing/_index.md +++ b/content/doc/billing/_index.md @@ -22,6 +22,6 @@ aliases: {{< cards >}} {{< card link="/developers/doc/billing/analytics-consumption" title="Analytics and consumption" icon="chart-bar" >}} {{< card link="/developers/doc/billing/payments-invoicing" title="Payments & invoicing" icon="banknotes" >}} - {{< card link="" title="Pricing" icon="currency-euro" >}} + {{< card link="https://www.clever.cloud/pricing/" title="Pricing" icon="currency-euro" >}} {{< card link="/developers/doc/billing/unified-invoices" title="Unified invoicing" icon="user-group" >}} {{< /cards >}} diff --git a/content/doc/ci-cd/custom-scripts.md b/content/doc/ci-cd/custom-scripts.md index 9d7957c41..3e2ef919b 100644 --- a/content/doc/ci-cd/custom-scripts.md +++ b/content/doc/ci-cd/custom-scripts.md @@ -61,7 +61,7 @@ before_script: # Download clever-tools before any script executes ## 🎓 Go further {{< cards >}} - {{< card link="" title="GitHub variables" subtitle="List of GitHub default CI/CD variables" icon="github" >}} - {{< card link="" title="GitLab variables" subtitle="List of GitLab predefined variables" icon="gitlab" >}} + {{< card link="https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables" title="GitHub variables" subtitle="List of GitHub default CI/CD variables" icon="github" >}} + {{< card link="https://docs.gitlab.com/ee/ci/variables/predefined_variables.html" title="GitLab variables" subtitle="List of GitLab predefined variables" icon="gitlab" >}} {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Deploy and manage your applications and dependencies from your terminal." icon="command-line" >}} {{< /cards >}} diff --git a/content/doc/ci-cd/github.md b/content/doc/ci-cd/github.md index 32328fa6e..8a9070f91 100644 --- a/content/doc/ci-cd/github.md +++ b/content/doc/ci-cd/github.md @@ -84,6 +84,6 @@ If you encounter troubles or bugs using the GitHub Action, feel free to open an ## 🎓 Go further {{< cards >}} - {{< card link="" title="GitHub Actions documentation" subtitle="Find help in GitHub Actions documentation" icon="github" >}} + {{< card link="https://docs.github.com/en/actions" title="GitHub Actions documentation" subtitle="Find help in GitHub Actions documentation" icon="github" >}} {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Deploy and manage your applications and dependencies from your terminal." icon="command-line" >}} {{< /cards >}} diff --git a/content/doc/ci-cd/gitlab.md b/content/doc/ci-cd/gitlab.md index f58af701a..ae5ed726f 100644 --- a/content/doc/ci-cd/gitlab.md +++ b/content/doc/ci-cd/gitlab.md @@ -86,6 +86,6 @@ If you encounter troubles or bugs using the GitLab component, feel free to open ## 🎓 Go further {{< cards >}} - {{< card link="" title="GitLab Components" subtitle="Learn how to use GitLab components" icon="gitlab" >}} + {{< card link="https://docs.gitlab.com/ee/ci/components" title="GitLab Components" subtitle="Learn how to use GitLab components" icon="gitlab" >}} {{< card link="/developers/doc/cli" title="Clever Tools CLI" subtitle="Deploy and manage your applications and dependencies from your terminal." icon="command-line" >}} {{< /cards >}} diff --git a/content/guides/docs.md b/content/guides/docs.md index def109982..eb49145bc 100644 --- a/content/guides/docs.md +++ b/content/guides/docs.md @@ -292,5 +292,5 @@ AWS_RESPONSE_CHECKSUM_VALIDATION="when_required" ## 🎓 Further Help {{< cards >}} - {{< card link="" title="Docs documentation" subtitle="Installation instructions" icon="adjustments-horizontal" >}} + {{< card link="https://github.com/suitenumerique/docs/blob/main/docs/installation/kubernetes.md#preparation" title="Docs documentation" subtitle="Installation instructions" icon="adjustments-horizontal" >}} {{< /cards >}} diff --git a/content/guides/docusaurus.md b/content/guides/docusaurus.md index fe9c756d3..5b7f00be2 100644 --- a/content/guides/docusaurus.md +++ b/content/guides/docusaurus.md @@ -45,5 +45,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="" title="Learn Docusaurus" subtitle="How to write and organize your content" icon="docusaurus" >}} + {{< card link="https://docusaurus.io/docs" title="Learn Docusaurus" subtitle="How to write and organize your content" icon="docusaurus" >}} {{< /cards >}} diff --git a/content/guides/eleventy.md b/content/guides/eleventy.md index ab4bded01..38e874910 100644 --- a/content/guides/eleventy.md +++ b/content/guides/eleventy.md @@ -47,5 +47,5 @@ clever env set CC_BUILD_COMMAND "npx @11ty/eleventy" {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="" title="Learn Eleventy (11ty)" subtitle="How to write and organize your content" icon="11ty" >}} + {{< card link="https://www.11ty.dev/docs/" title="Learn Eleventy (11ty)" subtitle="How to write and organize your content" icon="11ty" >}} {{< /cards >}} diff --git a/content/guides/ghost.md b/content/guides/ghost.md index 9220aa68d..629f8ac8f 100644 --- a/content/guides/ghost.md +++ b/content/guides/ghost.md @@ -273,7 +273,7 @@ The rebuild downloads the selected release and installs dependencies for the con ## Learn more {{< cards >}} - {{< card link="" title="Ghost documentation" subtitle="Configure and manage a Ghost publication" icon="ghost" >}} + {{< card link="https://docs.ghost.org/" title="Ghost documentation" subtitle="Configure and manage a Ghost publication" icon="ghost" >}} {{< card link="/developers/doc/applications/linux" title="Linux application runtime" subtitle="Configure and deploy any application" icon="linux" >}} {{< card link="/developers/doc/addons/mysql" title="MySQL" subtitle="Explore managed MySQL databases" icon="mysql" >}} {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="Explore S3-compatible object storage" icon="cellar" >}} diff --git a/content/guides/hexo.md b/content/guides/hexo.md index 358fee64c..e85fd50f3 100644 --- a/content/guides/hexo.md +++ b/content/guides/hexo.md @@ -52,5 +52,5 @@ clever env set CC_BUILD_COMMAND "npm run build" {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="" title="Learn Hexo" subtitle="How to write and organize your content" icon="hexo" >}} + {{< card link="https://hexo.io/docs/" title="Learn Hexo" subtitle="How to write and organize your content" icon="hexo" >}} {{< /cards >}} diff --git a/content/guides/hono.md b/content/guides/hono.md index 6b4662123..193015f4b 100644 --- a/content/guides/hono.md +++ b/content/guides/hono.md @@ -125,5 +125,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/nodejs" title="Deploy a Node.js application" subtitle="Learn more on deploying a Node.js application" icon="node" >}} - {{< card link="" title="Hono documentation" subtitle="Learn more about the Hono framework" icon="hono" >}} + {{< card link="https://hono.dev/docs" title="Hono documentation" subtitle="Learn more about the Hono framework" icon="hono" >}} {{< /cards >}} diff --git a/content/guides/hugo.md b/content/guides/hugo.md index 36f1115a9..ec655a365 100644 --- a/content/guides/hugo.md +++ b/content/guides/hugo.md @@ -54,5 +54,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="" title="Learn Hugo" subtitle="How to write and organize your content" icon="hugo" >}} + {{< card link="https://gohugo.io/documentation/" title="Learn Hugo" subtitle="How to write and organize your content" icon="hugo" >}} {{< /cards >}} diff --git a/content/guides/lume-deno.md b/content/guides/lume-deno.md index a816b4aa9..399a83141 100644 --- a/content/guides/lume-deno.md +++ b/content/guides/lume-deno.md @@ -59,5 +59,5 @@ It uses [Mise package manager](/doc/reference/reference-environment-variables/#i {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="" title="Learn Lume" subtitle="How to write and organize your content" icon="deno" >}} + {{< card link="https://lume.land/docs/overview/about-lume/" title="Learn Lume" subtitle="How to write and organize your content" icon="deno" >}} {{< /cards >}} diff --git a/content/guides/maudit.md b/content/guides/maudit.md index a1716dfd2..57c89696f 100644 --- a/content/guides/maudit.md +++ b/content/guides/maudit.md @@ -88,5 +88,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/static/" title="Static applications" subtitle="Configure and deploy static applications" icon="static" >}} - {{< card link="" title="Maudit documentation" subtitle="Build static websites with Maudit and Rust" icon="maudit" >}} + {{< card link="https://maudit.org/docs/" title="Maudit documentation" subtitle="Build static websites with Maudit and Rust" icon="maudit" >}} {{< /cards >}} diff --git a/content/guides/mdbook.md b/content/guides/mdbook.md index 54b7eebd8..16d4cd569 100644 --- a/content/guides/mdbook.md +++ b/content/guides/mdbook.md @@ -43,5 +43,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="How to configure your website" icon="static" >}} - {{< card link="" title="Learn mdBook" subtitle="How to write and organize your content" icon="mdbook" >}} + {{< card link="https://rust-lang.github.io/mdBook/" title="Learn mdBook" subtitle="How to write and organize your content" icon="mdbook" >}} {{< /cards >}} diff --git a/content/guides/mkdocs.md b/content/guides/mkdocs.md index c7683aedb..14eea59dd 100644 --- a/content/guides/mkdocs.md +++ b/content/guides/mkdocs.md @@ -48,5 +48,5 @@ clever scale --build-flavor M {{< cards >}} {{< card link="/developers/doc/applications/static" title="Static runtime documentation" subtitle="How to configure your website" icon="static" >}} - {{< card link="" title="MkDocs documentation" subtitle="How to write and organize your content" icon="docs" >}} + {{< card link="https://www.mkdocs.org/getting-started/" title="MkDocs documentation" subtitle="How to write and organize your content" icon="docs" >}} {{< /cards >}} diff --git a/content/guides/moodle.md b/content/guides/moodle.md index 845d95cce..d05265990 100644 --- a/content/guides/moodle.md +++ b/content/guides/moodle.md @@ -143,7 +143,7 @@ You might encounter errors when the Cron tries to access `moodledata` in your FS {{< card link="/developers/doc/administrate/cron" title="CRON" subtitle="Set up a CRON job for your app" icon="code-bracket" >}} {{< card link="/developers/doc/addons/fs-bucket" title="FS Bucket" subtitle="External File System for your apps" icon="fsbucket" >}} {{< card link="/developers/doc/addons/mysql" title="MySQL" icon="mysql" subtitle="Your self-hosted managed relational database" >}} - {{< card link="" title="Moodle Documentation" subtitle="Check Moodle installation guide" icon="moodle" >}} + {{< card link="https://docs.moodle.org/en/Installation_quick_guide" title="Moodle Documentation" subtitle="Check Moodle installation guide" icon="moodle" >}} {{< /cards >}} See [Moodle installation documentation](https://docs.moodle.org/en/Installation_quick_guide) for further help and development configuration. diff --git a/content/guides/nuxt.md b/content/guides/nuxt.md index 901797471..fefcab028 100644 --- a/content/guides/nuxt.md +++ b/content/guides/nuxt.md @@ -95,5 +95,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/nodejs" title="Deploy a Node.js application" subtitle="Learn more on deploying a Node.js application" icon="node" >}} {{< card link="/developers/doc/applications/static" title="Deploy a Static application" subtitle="Learn more on deploying a Static application" icon="static" >}} - {{< card link="" title="Learn Nuxt" subtitle="How to write and organize your content" icon="nuxt" >}} + {{< card link="https://nuxt.com/docs/4.x/getting-started/introduction" title="Learn Nuxt" subtitle="How to write and organize your content" icon="nuxt" >}} {{< /cards >}} diff --git a/content/guides/outline.md b/content/guides/outline.md index 430676072..51525e4cc 100644 --- a/content/guides/outline.md +++ b/content/guides/outline.md @@ -267,5 +267,5 @@ Replace the example values with the versions you selected. Outline applies pendi {{< card link="/developers/doc/addons/postgresql" title="PostgreSQL" subtitle="Explore managed PostgreSQL databases" icon="pg" >}} {{< card link="/developers/doc/addons/redis" title="Redis" subtitle="Explore managed Redis databases" icon="redis" >}} {{< card link="/developers/doc/addons/cellar" title="Cellar" subtitle="Explore S3-compatible object storage" icon="fsbucket" >}} - {{< card link="" title="Outline hosting documentation" subtitle="Learn more about self-hosting Outline" icon="outline" >}} + {{< card link="https://docs.getoutline.com/s/hosting" title="Outline hosting documentation" subtitle="Learn more about self-hosting Outline" icon="outline" >}} {{< /cards >}} diff --git a/content/guides/sftpgo.md b/content/guides/sftpgo.md index f9f0082d8..ece810004 100644 --- a/content/guides/sftpgo.md +++ b/content/guides/sftpgo.md @@ -271,5 +271,5 @@ Use any SFTP client with the following settings: {{< card link="/developers/doc/applications/linux" title="Linux applications" subtitle="Deploy any application" icon="linux" >}} {{< card link="/developers/doc/addons/cellar" title="Cellar object storage" subtitle="Store files with an S3 API" icon="database" >}} {{< card link="/developers/doc/addons/fs-bucket" title="FS Buckets" subtitle="Store files with SFTP access" icon="fsbucket" >}} - {{< card link="" title="SFTPGo documentation" subtitle="Configure users and storage" icon="book-open" >}} + {{< card link="https://docs.sftpgo.com/latest/" title="SFTPGo documentation" subtitle="Configure users and storage" icon="book-open" >}} {{< /cards >}} diff --git a/content/guides/sveltekit.md b/content/guides/sveltekit.md index f8d57b06f..54e28dd2b 100644 --- a/content/guides/sveltekit.md +++ b/content/guides/sveltekit.md @@ -228,5 +228,5 @@ clever domain add your.website.tld {{< cards >}} {{< card link="/developers/doc/applications/nodejs/" title="Node.js applications" subtitle="Configure and deploy Node.js applications" icon="node" >}} {{< card link="/developers/doc/applications/static/" title="Static applications" subtitle="Configure and deploy static applications" icon="static" >}} - {{< card link="" title="SvelteKit adapters" subtitle="Choose and configure a SvelteKit adapter" icon="svelte" >}} + {{< card link="https://svelte.dev/docs/kit/adapters" title="SvelteKit adapters" subtitle="Choose and configure a SvelteKit adapter" icon="svelte" >}} {{< /cards >}} diff --git a/content/guides/tutorial-laravel.md b/content/guides/tutorial-laravel.md index d9692b4fe..ef50ed4dc 100644 --- a/content/guides/tutorial-laravel.md +++ b/content/guides/tutorial-laravel.md @@ -176,5 +176,5 @@ This environment variable exists in any Clever Cloud instance. This configuratio {{< card link="/developers/doc/metrics/new-relic" title="Monitor your App with New Relic" subtitle="Connect your application to New Relic." icon="new-relic" >}} {{< card link="/developers/doc/addons/fs-bucket" title="FS Bucket" subtitle="External File System for your apps" icon="fsbucket" >}} {{< card link="/developers/doc/develop/build-hooks" title="Deployment hooks" subtitle="Learn about custom commands used in this guide." icon="rocket-launch" >}} - {{< card link="" title="Laravel Documentation" subtitle="Read Laravel documentation on deployments." icon="laravel" >}} + {{< card link="https://laravel.com/docs/10.x/deployment" title="Laravel Documentation" subtitle="Read Laravel documentation on deployments." icon="laravel" >}} {{< /cards >}} From b6c6ef2191a93915f4e58b3b876aafa90db78c08 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 11:03:13 +0200 Subject: [PATCH 163/214] fix(layouts): restore the skip-link anchor and copy-page block in changelog --- layouts/404.html | 2 +- layouts/changelog/list.html | 2 +- layouts/changelog/single.html | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/layouts/404.html b/layouts/404.html index 24e760c53..1fc8a84dc 100644 --- a/layouts/404.html +++ b/layouts/404.html @@ -2,7 +2,7 @@
-
Clever Cloud 404 Logo diff --git a/layouts/changelog/list.html b/layouts/changelog/list.html index 980000851..8052f78b9 100644 --- a/layouts/changelog/list.html +++ b/layouts/changelog/list.html @@ -4,7 +4,7 @@
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
-
+

{{ if .Title }}

{{ .Title }}

{{ end }}
diff --git a/layouts/changelog/single.html b/layouts/changelog/single.html index bfc300bb6..422bc669d 100644 --- a/layouts/changelog/single.html +++ b/layouts/changelog/single.html @@ -3,9 +3,14 @@ {{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" false) }} {{ partial "toc.html" . }}
-
+
{{ partial "breadcrumb.html" (dict "page" . "enable" true) }} - {{ if .Title }}

{{ .Title }}

{{ end }} + {{ if .Title }} +
+

{{ .Title }}

+ {{ partial "components/page-context-menu" . }} +
+ {{ end }}
{{- with $date := .Date }}{{ partial "utils/format-date" $date }}{{ end -}} {{- $lazyLoading := site.Params.enableImageLazyLoading | default true -}} From 070255654b8426018020b6f9378ea8bb387dfa66 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Wed, 2 Sep 2026 11:27:57 +0200 Subject: [PATCH 164/214] feat(ci): add an offline internal link and anchor checker --- check-internal-links.py | 1118 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1118 insertions(+) create mode 100755 check-internal-links.py diff --git a/check-internal-links.py b/check-internal-links.py new file mode 100755 index 000000000..ce1751b63 --- /dev/null +++ b/check-internal-links.py @@ -0,0 +1,1118 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [] +# /// +"""Check internal hyperlinks in a built Hugo site. + +Scope: only. Assets (img/src, srcset, link, script) have different +semantics and exemptions and are deliberately left to the deployed +`linkchecker` job. + +Complements that job, which follows redirects and checks external URLs but +never validates URL fragments. This one runs offline on the build output +and reports what it cannot see: + + broken the target page does not exist + dead-alias the link lands on an alias whose chain ends nowhere + alias-loop the redirect chain loops back on itself + lost-anchor the link lands on an alias AND carries a fragment; a Hugo + alias is a with no JavaScript, + which drops the fragment, so the reader lands at the top + anchor the target exists but has no matching id + redirect the link lands on an alias, so it costs an extra hop (note) + +Same-origin links outside this build are served by another application and +are simply set aside: `linkchecker` already covers them in CI. + +`source_locations` are heuristic candidates: the Markdown lines whose text +contains the offending URL. They can miss a link produced by a template or +by hugo.yaml, so `rendered_pages` is always reported as well. + +Notes are summarised as counts by default and only listed with -d: they +need no action here. `--format json` emits a stable machine-readable report +on stdout for tooling and agents; diagnostics go to stderr. + +Resolution runs against an exact, case-sensitive index of the files in the +build. macOS is case-insensitive and Linux is not, so an `is_file()` probe +would accept /doc/Foo locally and 404 in CI; the index makes both agree. +URL paths are percent-decoded and normalised before the prefix boundary is +tested, so a link climbing out of the prefix with `..` cannot be validated +against the wrong file. + +Ownership: a path under the baseURL prefix belongs to this build and must +resolve, unless it is declared with --external-prefix (other applications +are deployed under the same prefix). + +The HTML element is not honoured; the build contains none, and +supporting it would need its own same-origin and ownership checks. + +Usage: + hugo # writes to publishDir + ./check-internal-links.py # defaults to public/developers + uv run check-internal-links.py -d # also list the non-blocking notes + uv run check-internal-links.py --format json # machine-readable, stdout only + uv run check-internal-links.py --root some/dir --prefix /developers + +Exit codes: 0 clean, 1 problems found, 2 bad invocation or missing build. +""" + +from __future__ import annotations + +import argparse +import difflib +import json +import os +import posixpath +import re +import shutil +import sys +from collections import Counter, defaultdict +from concurrent.futures import ProcessPoolExecutor +from concurrent.futures.process import BrokenProcessPool +from html.parser import HTMLParser +from pathlib import Path +from time import perf_counter +from typing import TYPE_CHECKING, NamedTuple, NoReturn + +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence + + Attrs = Sequence[tuple[str, str | None]] +from urllib.parse import unquote, urljoin, urlsplit, urlunsplit + +# Paths served under the baseURL prefix by something other than this build. +DEFAULT_EXTERNAL = ("/developers/clever-components",) +# Pages whose anchors are generated client-side, absent from static HTML. +DEFAULT_SKIP_ANCHORS = ("api/v2",) +DEFAULT_SOURCES = ("content", "shared") +DEFAULT_CONFIGS = ("hugo.yaml",) + +SKIPPED_SCHEMES = ("mailto:", "tel:", "javascript:", "data:") +DEFAULT_PORTS = {"http": "80", "https": "443"} +ALIAS_HOP_LIMIT = 10 # a Hugo alias chain is normally one hop +MAX_SUGGESTIONS = 5 # near-miss anchors offered as a fix +SUGGESTION_CUTOFF = 0.4 # loose enough to catch prefix- -> path-routing +MAX_SOURCE_LINES = 4 # source lines printed per finding +MAX_PAGES_LISTED = 2 # rendered pages printed before "+N more" +CHUNKS_PER_WORKER = 4 # how finely the page list is split across workers +CHUNK_CEILING = 64 # never hand a worker more than this in one go +HELP_WIDTH = 96 # usage and help never wrap wider than this + +EXIT_OK, EXIT_FINDINGS, EXIT_MISUSE = 0, 1, 2 +GLYPHS = "✓✗·→" # dropped together when the stream cannot encode them + + +class Kind(NamedTuple): + """A category of finding: how to name it, explain it, and how bad it is.""" + + name: str + title: str + explanation: str + blocking: bool + + +KINDS: tuple[Kind, ...] = ( + Kind( + "broken", + "Broken targets", + "The target does not exist in the build.", + blocking=True, + ), + Kind( + "dead-alias", + "Redirects to missing pages", + "The link lands on a redirect whose own destination is missing.", + blocking=True, + ), + Kind( + "alias-loop", + "Redirect loops", + "The chain of redirects comes back on itself and never lands.", + blocking=True, + ), + Kind( + "lost-anchor", + "Fragments lost in redirects", + "A redirect cannot carry a #fragment: the reader lands at the top.", + blocking=True, + ), + Kind( + "anchor", + "Missing anchors", + "The page exists but has no element with that id, usually a renamed heading.", + blocking=True, + ), + Kind( + "redirect", + "Redirects", + "These resolve through an alias. Point them at the destination to save a hop.", + blocking=False, + ), +) +KIND_ORDER = [kind.name for kind in KINDS] +BLOCKING = frozenset(kind.name for kind in KINDS if kind.blocking) +NOTE_LABEL = {"redirect": "redirecting"} + + +# -------------------------------------------------------------------------- +# Small pure helpers +# -------------------------------------------------------------------------- + + +def canon_origin(scheme: str, netloc: str) -> str: + """Canonical scheme://host, lowercased and without a default port.""" + scheme = (scheme or "").lower() + host = (netloc or "").lower() + port = DEFAULT_PORTS.get(scheme) + if port and host.endswith(f":{port}"): + host = host[: -len(port) - 1] + return f"{scheme}://{host}" if host else "" + + +def parse_refresh(content: str) -> str | None: + """Target of a zero-delay meta refresh, else None. + + Only a zero delay is a Hugo alias. A page with a real timed refresh is + a normal page and must still be crawled. + """ + delay, separator, rest = content.partition(";") + if not separator: + return None + try: + if float(delay.strip()) != 0: + return None + except ValueError: + return None + rest = re.sub(r"^url\s*=\s*", "", rest.strip(), count=1, flags=re.IGNORECASE) + if rest[:1] == rest[-1:] and rest[:1] in ("'", '"'): + rest = rest[1:-1] + return rest.strip() or None + + +def wanted_anchor(fragment: str) -> str | None: + """The id a fragment targets, or None when it targets no id. + + `#top` scrolls to the top of the document with no element of that name, + and a `:~:` text directive is not an id at all. + """ + frag = unquote(fragment) + if ":~:" in frag: + frag = frag.split(":~:", 1)[0] + if not frag or frag.lower() == "top": + return None + return frag + + +def under(path: str, prefix: str) -> bool: + """True if `path` equals `prefix` or lives under it. Never a raw startswith.""" + return path == prefix or path.startswith(prefix.rstrip("/") + "/") + + +def display_path(path: str) -> str: + """Path relative to the invocation directory, in POSIX form. + + Always relative when it can be computed: an absolute path would leak + the local layout into a published JSON artifact and make the report + differ between runners. + """ + try: + return os.path.relpath(path, Path.cwd()).replace(os.sep, "/") + except ValueError: # different volume on Windows + return path.replace(os.sep, "/") + + +def plural(count: int, singular: str, many: str | None = None) -> str: + return f"{count:,} {singular if count == 1 else (many or singular + 's')}" + + +def cpu_limit() -> int: + return os.cpu_count() or 1 + + +# -------------------------------------------------------------------------- +# Parsing +# -------------------------------------------------------------------------- + + +class Page(HTMLParser): + """Collect hyperlinks, anchor ids and any meta refresh from one page. + + A real parser rather than a regex: it handles uppercase attributes, + single or unquoted values and character references. It also puts +