---
title: Connecting Scaleway Managed Databases to Kubernetes Kapsule clusters
description: This page explains how to connect Scaleway Managed Databases to Kubernetes Kapsule clusters
tags: managed database kubernetes cluster kapsule k8s
dates:
  validation: 2025-10-01
  posted: 2025-03-26
---
import Requirements from '@macros/iam/requirements.mdx'


This guide explains how to set up and connect a Scaleway Managed Database for PostgreSQL or MySQL with a Scaleway Kubernetes Kapsule cluster.

We will walk you through the entire process using both the Scaleway CLI and Terraform approaches.

<Requirements />

- A Scaleway account logged into the [console](https://console.scaleway.com)
- [Owner](/iam/concepts/#owner) status or [IAM permissions](/iam/concepts/#permission) allowing you to perform actions in the intended Organization
- A valid [API key](/iam/how-to/create-api-keys/)
- [Scaleway CLI](https://github.com/scaleway/scaleway-cli) installed and configured
- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed
- [Terraform](https://www.terraform.io/downloads.html) or [OpenTofu](https://opentofu.org/) installed (for Terraform approach)

## Method 1 - Using the Scaleway CLI

First, [install the Scaleway CLI](/scaleway-cli/quickstart/#how-to-install-the-scaleway-cli-locally), and use `scw init` to set your API key and `scw config set region par1` to set the default region (e.g. Paris).

### Creating a Private Network

Create a Private Network that both your Kubernetes cluster and database will use:

    ```
    scw vpc private-network create name=kube-db-network
    ```

    <Message type="note">
      Note the Private Network ID from the output for later use.
    </Message>

### Creating a Managed Database Instance

1. Run the following command to create a Managed PostgreSQL (or MySQL) Database Instance:

    ```
    scw rdb instance create \
    name=my-kube-database \
    node-type=db-dev-s \
    engine=PostgreSQL-15 \
    is-ha-cluster=true \
    user-name=admin \
    password=StrongP@ssw0rd123 \
    region=fr-par
    ```

    This creates a high-availability PostgreSQL 15 database with a public endpoint.

    <Message type="important">
      At this point the database is exposed to the Internet.
    </Message>

3. Add the Private Network endpoint to the database:

    ```
    scw rdb endpoint create \
    <database-instance-id> \
    private-network.private-network-id=<private-network-id> \
    private-network.enable-ipam=true region-fr-par
    ```

4. Get the Instance details and look for the public endpoint ID under the "Endpoints" section.
    ```
    scw rdb instance get <database-instance-id>
    ```

4. Remove the public endpoint to ensure the database is only reachable from the Private Network and no longer exposed to the public Ineternet.
    ```
    scw rdb endpoint delete instance-id=<database-instance-id> <public-endpoint-id>
    ```

### Creating a Kubernetes Kapsule cluster

1. Run the following Scaleway CLI command to create a Kubernetes Kapsule cluster attached to the same Private Network:

    ```
    scw k8s cluster create \
    name=my-kube-cluster \
    type=kapsule \
    version=1.28.2 \
    cni=cilium \
    pools.0.name=default \
    pools.0.node-type=DEV1-M \
    pools.0.size=2 \
    pools.0.autoscaling=true \
    pools.0.min-size=2 \
    pools.0.max-size=5 \
    private-network-id=<private-network-id>
    ```

2. Wait for the cluster to be ready, then get the `kubeconfig`:

    ```
    scw k8s kubeconfig install <k8s-cluster-id> region=fr-par
    ```

### Creating a Kubernetes secret for database credentials

Use `kubectl` to create a Kubernetes secret to store the database credentials:

    ```
    kubectl create secret generic db-credentials \
    --from-literal=DB_HOST=<private-network-db-hostname> \
    --from-literal=DB_PORT=5432 \
    --from-literal=DB_NAME=rdb \
    --from-literal=DB_USER=admin \
    --from-literal=DB_PASSWORD=StrongP@ssw0rd123
    ```

### Deploying a sample application

1. Create a Kubernetes deployment that will connect to the database. Save this as `db-app.yaml`:

    ```
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: postgres-client
    spec:
    replicas: 1
    selector:
        matchLabels:
        app: postgres-client
    template:
        metadata:
        labels:
            app: postgres-client
        spec:
        containers:
        - name: postgres-client
            image: postgres:latest
            command: ["sleep", "infinity"]
            env:
            - name: DB_HOST
            valueFrom:
                secretKeyRef:
                name: db-credentials
                key: DB_HOST
            - name: DB_PORT
            valueFrom:
                secretKeyRef:
                name: db-credentials
                key: DB_PORT
            - name: DB_NAME
            valueFrom:
                secretKeyRef:
                name: db-credentials
                key: DB_NAME
            - name: DB_USER
            valueFrom:
                secretKeyRef:
                name: db-credentials
                key: DB_USER
            - name: DB_PASSWORD
            valueFrom:
                secretKeyRef:
                name: db-credentials
                key: DB_PASSWORD
    ```
2. Apply it to your cluster:

    ```
    kubectl apply -f db-app.yaml
    ```

3. Check that your application can connect to the database:

    ```
    kubectl logs -f deployment/postgres-client
    ```

## Method 2 - Using Terraform

For a more infrastructure-as-code approach, you can use Terraform or OpenTofu (open-source Terraform fork) to set up the same resources.
Install Terraform and ensure the Scaleway Terraform provider is set up with `terraform init -provider=scaleway/scaleway`.

### Setting-up Terraform files

1. Create a new directory and set up your files:
    ```
    mkdir scaleway-kube-db
    cd scaleway-kube-db
    ```

2. Create a `providers.tf` file:
    ```
    terraform {
    required_providers {
        scaleway = {
        source  = "scaleway/scaleway"
        version = "~> 2.40"
        }
    }
    }

    provider "scaleway" {
    access_key      = var.scaleway_access_key
    secret_key      = var.scaleway_secret_key
    project_id      = var.project_id
    region          = var.region
    zone            = var.zone
    }
    ```

3. Create a `variables.tf` file:
    ```
    variable "scaleway_access_key" {
    description = "Scaleway Access Key"
    type        = string
    sensitive   = true
    }

    variable "scaleway_secret_key" {
    description = "Scaleway Secret Key"
    type        = string
    sensitive   = true
    }

    variable "project_id" {
    description = "Scaleway Project ID"
    type        = string
    }

    variable "region" {
    description = "Scaleway region (e.g., fr-par)"
    type        = string
    default     = "fr-par"
    }

    variable "zone" {
    description = "Scaleway zone (e.g., fr-par-1)"
    type        = string
    default     = "fr-par-1"
    }

    variable "db_password" {
    description = "Password for database user"
    type        = string
    sensitive   = true
    }

    variable "db_user" {
    description = "Database username"
    type        = string
    default     = "admin"
    }
    ```

4. Create a `main.tf` file for the infrastructure:
    ```
    # Create Private Network
    resource "scaleway_vpc_private_network" "private_net" {
    name   = "kube-db-network"
    region = var.region
    }

    # Create Managed PostgreSQL Database
    resource "scaleway_rdb_instance" "database" {
    name          = "my-kube-database"
    node_type     = "db-dev-s"
    engine        = "PostgreSQL-15"
    is_ha_cluster = true
    user_name     = var.db_user
    password      = var.db_password

    private_network {
        pn_id       = scaleway_vpc_private_network.private_net.id
        enable_ipam = true
    }
    }

    # Kubernetes Cluster (Kapsule)
    resource "scaleway_k8s_cluster" "kapsule" {
    name                     = "my-kube-cluster-${random_id.suffix.hex}" # Make the name unique
    version                  = "1.28.2"
    cni                      = "cilium"
    private_network_id       = scaleway_vpc_private_network.private_net.id
    delete_additional_resources = true
    }

    # Kubernetes Node Pool
    resource "scaleway_k8s_pool" "default_pool" {
    cluster_id       = scaleway_k8s_cluster.kapsule.id
    name            = "default-pool"
    node_type       = "DEV1-M"
    size            = 2
    autoscaling     = true
    min_size        = 2
    max_size        = 5
    autohealing     = true
    container_runtime = "containerd"
    }

    # Generate a random suffix for uniqueness
    resource "random_id" "suffix" {
    byte_length = 4
    }

    # Output Database Connection Information
    output "db_host" {
    value = scaleway_rdb_instance.database.private_network[0].ip
    }

    output "db_port" {
    value = scaleway_rdb_instance.database.db_host_port
    }

    output "kubeconfig" {
    value     = scaleway_k8s_cluster.kapsule.kubeconfig
    sensitive = true
    }
    ```
### Creating a terraform.tfvars file

Create a `terraform.tfvars` file to store your variables securely:
    ```
    scaleway_access_key = "<your-scaleway-access-key>"
    scaleway_secret_key = "<your-scaleway-secret-key>"
    project_id          = "<your-scaleway-project-id>"
    db_password         = "<your-strong-db-password>"
    ```

### Applying the Terraform configuration

Initialize and apply the Terraform configuration:

    ```
    terraform init
    terraform apply
    ```

    After confirming the plan, Terraform will create all the resources and output the database endpoint.

## Connecting a real application

Now let's deploy a more realistic application that uses the database. Here's a simple Node.js application with Express and pg (PostgreSQL client):

### Creating a Dockerfile for the application

The Dockerfile is used to create a Docker image for your application. Here's a simple example:

    ```dockerfile
    # Use the official Node.js 14 image.
    # https://hub.docker.com/_/node
    FROM node:14

    # Create and change to the app directory.
    WORKDIR /usr/src/app

    # Copy application dependency manifests to the container image.
    # A wildcard is used to ensure both package.json AND package-lock.json are copied.
    # Copying this separately prevents re-running npm install on every code change.
    COPY package*.json ./

    # Install production dependencies.
    RUN npm install --only=production

    # Copy local code to the container image.
    COPY . .

    # Run the web service on container startup.
    CMD [ "node", "app.js" ]

    # Expose the port the app runs on
    EXPOSE 8080
    ```

### Creating the application files

You need to create the necessary files for your Node.js application. Here’s a simple `app.js` and a `package.json` file as an example:

    **`app.js`**:
    ```javascript
    const express = require('express');
    const { Pool } = require('pg');
    const app = express();

    // Get DB credentials from environment variables
    const pool = new Pool({
    user: process.env.DB_USER, // 'admin'
    host: process.env.DB_HOST, // '<private-network-db-hostname>'
    database: process.env.DB_NAME, // 'rdb'
    password: process.env.DB_PASSWORD,
    port: process.env.DB_PORT, // '5432'
    });

    app.get('/', async (req, res) => {
    try {
        const result = await pool.query('SELECT NOW() as now');
        res.send(result.rows);
    } catch (err) {
        console.error(err);
        res.status(500).send(err.toString());
    }
    });

    const PORT = process.env.PORT || 8080;
    app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
    });
    ```

    **`package.json`**:
    ```json
    {
    "name": "node-postgres-app",
    "version": "1.0.0",
    "main": "app.js",
    "dependencies": {
        "express": "^4.17.1",
        "pg": "^8.6.0"
    }
    }
    ```

### Creating Kubernetes manifests for the application

1. Ensure the previously created secret is cleared:
```
kubectl delete secret db-credentials
```

2. Recreate the Secret Using `kubectl create secret`. Run the following command without any base64 encoding:
    ```
    kubectl create secret generic db-credentials \
    --from-literal=DB_HOST=<private-network-db-hostname> \
    --from-literal=DB_PORT=5432 \
    --from-literal=DB_NAME=rdb \
    --from-literal=DB_USER=admin \
    --from-literal=DB_PASSWORD=StrongP@ssw0rd123
    ```
    Kubernetes will automatically handle the base64 encoding for you.

3. Get the secret details:
    ```
    kubectl get secret db-credentials -o yaml
    ```

4. Create two main Kubernetes manifests: one for the deployment and one for the service.

    **`deployment.yaml`**:
    ```yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: node-postgres-app
    spec:
    replicas: 1
    selector:
        matchLabels:
        app: node-postgres-app
    template:
        metadata:
        labels:
            app: node-postgres-app
        spec:
        containers:
            - name: node-postgres-app
            image: ${YOUR_DOCKER_REGISTRY}/node-postgres-app:latest
            ports:
                - containerPort: 8080
            env:
                - name: DB_HOST
                valueFrom:
                    secretKeyRef:
                    name: db-credentials
                    key: DB_HOST
                - name: DB_PORT
                valueFrom:
                    secretKeyRef:
                    name: db-credentials
                    key: DB_PORT
                - name: DB_NAME
                valueFrom:
                    secretKeyRef:
                    name: db-credentials
                    key: DB_NAME
                - name: DB_USER
                valueFrom:
                    secretKeyRef:
                    name: db-credentials
                    key: DB_USER
                - name: DB_PASSWORD
                valueFrom:
                    secretKeyRef:
                    name: db-credentials
                    key: DB_PASSWORD
    ---
    apiVersion: v1
    kind: Secret
    metadata:
    name: db-credentials
    type: Opaque
    data:
    DB_HOST: <base64-encoded-db-host>
    DB_PORT: <base64-encoded-db-port>
    DB_NAME: <base64-encoded-db-name>
    DB_USER: <base64-encoded-db-user>
    DB_PASSWORD: <base64-encoded-db-password>
    ```

    **`service.yaml`**:
    ```yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: node-postgres-app
    spec:
    type: LoadBalancer
    ports:
    - port: 80
        targetPort: 8080
    selector:
        app: node-postgres-app
    ```
### Building and pushing the Docker image

    <Message type="note">
        Replace `${YOUR_DOCKER_REGISTRY}` with your Docker registry (e.g., Docker Hub username).
    </Message>

    ```
    docker build -t ${YOUR_DOCKER_REGISTRY}/node-postgres-app:latest .
    docker push ${YOUR_DOCKER_REGISTRY}/node-postgres-app:latest
    ```

### Deploying the application to Kubernetes

1. Apply the Kubernetes manifests:

    ```
    kubectl apply -f deployment.yaml
    kubectl apply -f service.yaml
    ```

2. Check the service to get the external IP:

    ```
    kubectl get service node-postgres-app
    ```

3. Visit the application at the external IP to see it in action. If everything is set up correctly, you should see the current PostgreSQL time displayed when you access the application URL.

## Security best practices

### Use Private Networks

Always use Private Networks when connecting a Kubernetes cluster to a database. This ensures that database traffic never traverses the public internet, reducing the attack surface significantly.

### Implement proper TLS

If you need to use a public endpoint, ensure you're using TLS with certificate verification:

For PostgreSQL, add this to your connection string:

    ```
    sslmode=verify-full sslrootcert=/path/to/scaleway-ca.pem
    ```

### Restrict database access with network policies

Implement Kubernetes Network Policies to control which Pods can access the database:

### Use secrets management

Consider using a secrets management solution like HashiCorp Vault or Kubernetes External Secrets to manage database credentials instead of storing them directly in Kubernetes Secrets.

### Regularly rotate credentials

Implement a process to regularly rotate database credentials. This can be automated using tools like Vault or custom operators.