Skip to content

Kubernetes

This page describes how to run a self-managed instance of R2Devops on Kubernetes.

💻 Requirements

  • A Kubernetes cluster with:
    • One of these ingress controller:
    • A certificate manager with a ACME provider: cert-manager
    • Only if you run external services (PostgreSQL, Redis, MinIO) in Kubernetes: the ability to provision persistent volumes in your cluster
  • Your local environment with CLI to interact with Kubernetes API:
  • Write access to the DNS zone of the domain to use with R2Devops
  • A user account on the GitLab instance

🏗️: Infrastructure

The R2Devops Infrastructure is composed of the following components:

R2Devops infrastructure

The Helm chart used in this documentation allows installing all these services embedded in the chart as dependencies or to use external S3, PostgreSQL and/or Redis. Both alternatives are detailed below.

🛠️ Installation

📥 Initialize your cluster

  1. Choose a namespace name and set it in a variable
    export R2DEVOPS_NS="r2devops"
    
  2. Set secret variable provided by R2Devops in your shell
    export REGISTRY_TOKEN="REPLACE_ME"
    
  3. Create the namespace
    kubectl create ns $R2DEVOPS_NS
    
  4. Create the secret to authenticate on R2Devops registry
    kubectl create secret docker-registry r2devops-registry --docker-server=registry.gitlab.com/r2devops --docker-username=r2devops-user --docker-password=$REGISTRY_TOKEN -n $R2DEVOPS_NS
    
  5. Add R2Devops repo
    helm repo add r2devops https://charts.r2devops.io/
    

📄 Domain name

Info

You need a domain to run R2Devops. For example, if you have the domain name mydomain.com:

  • The frontend URL will be https://r2devops.mydomain.com
  • The backend URL will be https://api.r2devops.mydomain.com
  1. Create DNS records

    The application needs two DNS records to work properly:

    1. Frontend
      • Name: r2devops.<domain_name>
      • Type: A
      • Content: <your-cluster-public-ip>
    2. Backend
      • Name: api.r2devops.<domain_name>
      • Type: A
      • Content: <your-cluster-public-ip>

🦊 GitLab OIDC

R2Devops uses GitLab as an OIDC (OpenID Connect) provider to authenticate users. Let's see how to connect it to your GitLab instance.

  1. Choose a group on your GitLab instance to create an application. It can be any group. Open the chosen group in GitLab interface and navigate through Settings > Applications:

    Profile_Menu

  2. Then, create an application with the following information

    Warning

    You need to replace <API_URL> below with the API URL consistently with what you have configured as backend DNS record

    • Name: R2Devops self-managed
    • Redirect URI : https://<API_URL>/kratos/public/self-service/methods/oidc/callback/gitlab
    • Confidential: true (let the box checked)
    • Scopes: openid, email
  3. Click on Save Application and you should see the following screen:

    Application

  4. Store Application ID and Secret somewhere safe, we will need to use them in next step

⚙️ Configure values

This section describes how to configure your custom values file. The default values.yaml is available here. Two examples are available at the end of this documentation:

  1. Using only services embedded in the chart
  2. Using only external services

Note

For the following sections, we assume that your custom value file will be located in your current directory and be named custom_values.yaml

🔐 Secrets

This section is optional. You need to follow this section only if you want to store secrets values as kubernetes secrets instead of writing them in your custom value file.

  1. R2Devops secret

    Replace REDACTED by your R2Devops license key encoded in base64 and create following secret:

    apiVersion: v1
    kind: Secret
    metadata:
      name: r2devops-secret
      namespace: r2devops
    type: Opaque
    data:
      license-key: REDACTED
    

  2. MinIO secret

    Replace both REDACTED values by your S3 storage key and secret encoded in base64. If you want to use minIO embedded in this chart, choose the values.

    apiVersion: v1
    kind: Secret
    metadata:
      name: minio-secret
      namespace: r2devops
    type: Opaque
    data:
      root-user: REDACTED
      root-password: REDACTED
    

  3. PostgreSQL secret

    Replace REDACTED by your postgres password encoded in base64. If you want to use postgres embedded in this chart, choose the value.

    apiVersion: v1
    kind: Secret
    metadata:
      name: postgresql-secret
      namespace: r2devops
    type: Opaque
    data:
      password: REDACTED
    

  4. Redis secret

    Replace REDACTED by your redis password encoded in base64. If you want to use redis embedded in this chart, choose the value.

    apiVersion: v1
    kind: Secret
    metadata:
      name: redis-secret
      namespace: r2devops
    type: Opaque
    data:
      password: REDACTED
    

  5. Kratos secret

    Replace all REDACTED values by your kratos secrets encoded in base64

    apiVersion: v1
    kind: Secret
    metadata:
      name: kratos-secret
      namespace: r2devops
    type: Opaque
    data:
      dsn: REDACTED
      oidc: REDACTED
      secretCookie: REDACTED
      secretCipher: REDACTED
      secretDefault: REDACTED
      smtpConnectionURI: REDACTED
    

    1. dsn value

      Use the following format:

      postgres://r2devops:<password>@r2devops-postgresql:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4`
      

      Todo

      • Replace <password> by your postgres password
      • If you use an external postgresql (not the one embedded in this chart)
        • Replace r2devops-postgresql by your postgres host
        • Replace kratos by your postgres database for kratos
        • Replace sslmode=disable by mode you want to use (ex: sslmode=require)
        • Replace 5432 by your postgres port
    2. oidc value

      Use the following format:

      [
          {
              "id": "gitlab",
              "provider": "gitlab",
              "mapper_url": "file:///etc/config/oidc.gitlab.jsonnet",
              "scope": [
                  "openid",
                  "email"
              ],
              "client_id": "REPLACE_ME_BY_APPLICATION_ID",
              "client_secret": "REPLACE_ME_BY_APPLICATION_SECRET",
              "issuer_url": "https://gitlab.mydomain.com"
          }
      ]
      

      Todo

      • Replace REPLACE_ME_BY_APPLICATION_ID by Application ID get in GitLab OIDC step above
      • Replace REPLACE_ME_BY_APPLICATION_SECRET by Secret get in GitLab OIDC step above
      • Replace gitlab.mydomain.com by the domain of your GitLab instance
    3. secretCookie, secretCipher and secretDefault values

      Use a random secret with 32 alphanumeric char

    4. smtpConnectionURI value

      Use smtp://. This isn't used but mandatory

🤖 R2Devops

Add R2Devops related configuration in your new values file custom_values.yaml:

  1. Add license key (provided by R2Devops), R2Devops domain and GitLab domains

    License key

    If you do not have a license-key, you can let the variable PROJECTS_LIMIT_KEY empty (value: ""). Your R2Devops instance will be limited to 10 projects.

    front:
      host: "r2devops.mydomain.com"
    
    jobs:
      host: "api.r2devops.mydomain.com"
    
      # Not using secret for license (comment if you use secret)
      extraEnv:
        - name: PROJECTS_LIMIT_KEY
          value: "<license-key>"
    
      # Using existing secret for license (uncomment if you use secret)
      #extraEnv:
      #  - name: PROJECTS_LIMIT_KEY
      #    valueFrom:
      #      secretKeyRef:
      #        name: "r2devops-secret"
      #        key: "license-key"
    
    gitlab:
      domain: "https://gitlab.mydomain.com"
    
    worker:
      replicaCount: 10 # Default is 5. Increase it depending of your needs
    
  2. Add your Ingress configuration

    ingress:
      enabled: true
      className: ""   # Add class name for your ingress controller
      annotations: {} # Add anotation required by your ingress controller or certificate manager
    

📙 MinIO

You can choose between 2 options: use minIO embedded in this chart or use any external S3 service.

Add the following configuration (1 OR 2) in your custom_values.yaml file:

  1. Use minIO embedded in this chart

    minio:
      dependency:
        enabled: true
    
      # Not using secret for auth (comment if you use secret)
      auth:
        rootPassword: REPLACE_ME_BY_NEW_MINIO_PASSWORD
    
      # Using existing secret for auth (uncomment if you use secret)
      #auth:
      #  existingSecret: "minio-secret"
    
  2. Use an external S3 service (bucket must be created)

    minio:
      dependency:
        enabled: false
      custom:
        host: REPLACE_ME_BY_S3_ENDPOINT
        bucketName: REPLACE_ME_BY_S3_BUCKET_NAME
        region: REPLACE_ME_BY_BUCKET_REGION
        disableSSL: "false"
        forcePathStyle: "false"
    
      # Not using secret for auth (comment if you use secret)
      auth:
        rootUser: REPLACE_ME_BY_S3_ACCESS_KEY
        rootPassword: REPLACE_ME_BY_S3_SECRET_KEY
    
      # Using existing secret for auth (uncomment if you use secret)
      #auth:
      #  existingSecret: "minio-secret"
    

📘 PostgreSQL

You can choose between 2 options: use postgresql embedded in this chart or use an external postgresql.

Add following configuration (1 OR 2) in your custom_values.yaml file:

  1. Use postgresql embedded in this chart

    postgresql:
      dependency:
        enabled: true
      global:
        postgresql:
    
          # Not using secret for auth (comment if you use secret)
          auth:
            password: REPLACE_ME_BY_NEW_POSTGRESQL_PASSWORD
            postgresPassword: REPLACE_ME_BY_NEW_POSTGRESQL_PASSWORD
    
          # Using existing secret for auth (uncomment if you use secret)
          #auth:
          #  username: r2devops
          #  existingSecret: "postgresql-secret"
          #  secretKeys:
          #    adminPasswordKey: "password"
          #    userPasswordKey: "password"
    
  2. Use an external postgresql (database must be created)

    postgresql:
      dependency:
        enabled: false
      custom:
        host: REPLACE_ME_BY_POSTGRES_HOST
        dbName: REPLACE_ME_BY_POSTGRES_DB_NAME
        sslmode: "require"
        port: 5432
      global:
        postgresql:
    
          # Not using secret for auth (comment if you use secret)
          auth:
            username: REPLACE_ME_BY_POSTGRES_USERNAME
            postgresPassword: REPLACE_ME_BY_POSTGRES_PASSWORD
    
          # Using existing secret for auth password (uncomment if you use secret)
          #auth:
          #  username: r2devops
          #  existingSecret: "postgresql-secret"
          #  secretKeys:
          #    adminPasswordKey: "password"
          #    userPasswordKey: "password"
    

📕 Redis

You can choose between 2 options: use redis embedded in this chart or use an external redis.

Add following configuration (1 OR 2) in your custom_values.yaml file:

  1. Use redis embedded in this chart

    redis:
      dependency:
        enabled: true
    
      # Not using secret for auth (comment if you use secret)
      auth:
        password: REPLACE_ME_BY_NEW_REDIS_PASSWORD
    
      # Using existing secret for auth (uncomment if you use secret)
      #auth:
      #  existingSecret: "redis-secret"
      #  existingSecretPasswordKey: "password"
    
  2. Use an external redis (database must be created)

    redis:
      dependency:
        enabled: false
      custom:
        port: 6379
        host: REPLACE_ME_BY_REDIS_HOST
        user: REPLACE_ME_BY_REDIS_USENAME
        cert: |
          REPLACE_ME_BY_REDIS_TLS_CERTIFICATE
    
      # Not using secret for auth (comment if you use secret)
      auth:
        password: REPLACE_ME_BY_REDIS_PASSWORD
    
      # Using existing secret for auth (uncomment if you use secret)
      #auth:
      #  existingSecret: "redis-secret"
      #  existingSecretPasswordKey: "password"
    

📗 Kratos

We strongly recommend using the Kratos dependency embedded in this chart, we don't support external Kratos.

Add all following configuration chunks in your custom_values.yaml file.

Warning

Be careful to keep all indentation as set in code blocks

  1. Add ingress by copy/pasting below configuration:

    Todo

    • Replace mydomain.com by your domain name
    • Replace letsencrypt-production by your clusterIssuer name
    • If your ingress is not nginx or traefik, contact #support for help
    1. If you use nginx as ingress controller

      kratos:
        dependency:
          enabled: true
        ingress:
          public:
            annotations:
              cert-manager.io/cluster-issuer: "letsencrypt-production"
              nginx.ingress.kubernetes.io/rewrite-target: /$2
            className: "nginx"
            hosts:
              - host: "api.r2devops.mydomain.com"
                paths:
                  - path: /kratos/public(/|$)(.*)
                    pathType: ImplementationSpecific
            tls:
              - secretName: r2devops-jobs-tls
                hosts:
                  - "api.r2devops.mydomain.com"
      
    2. If you use traefik as ingress controller:

      First, create this middleware in your k8s cluster

      # Strip prefix /kratos/public
      # See https://doc.traefik.io/traefik/middlewares/http/stripprefix/
      
      apiVersion: traefik.containo.us/v1alpha1
      kind: Middleware
      metadata:
        name: stripprefix
        namespace: r2devops
      spec:
        stripPrefix:
          prefixes:
            - /kratos/public
      

      Then, add following lines in your custom_values.yaml file

      kratos:
        dependency:
          enabled: true
        ingress:
          public:
            annotations:
              cert-manager.io/cluster-issuer: "letsencrypt-production"
              traefik.ingress.kubernetes.io/router.middlewares: r2devops-stripprefix@kubernetescrd
            className: ""
            hosts:
              - host: "api.r2devops.mydomain.com"
                paths:
                  - path: /kratos/public
                    pathType: Prefix
      
  2. Continue Kratos configuration in your custom_values.yaml file

    Next steps of Kratos Configuration depends on if you want to use existing secrets. Choose between 2 followings methods and expand the right one:

    Without existing secret
    1. Add database configuration

      Todo

      • Replace password by value set in postgresql.global.postgresql.auth.postgresPassword above
      • If you use an external postgresql (not the one embedded in this chart)
        • Replace r2devops-postgresql by value set in postgresql.custom.host above
        • Replace kratos by value set in postgresql.custom.dbName above
        • Replace sslmode=disable by mode set in posgtres.custom.sslmode above (ex: sslmode=require)
        • Replace 5432 by value set in posgtresql.custom.port above
        kratos:
          config:
            dsn: "postgres://r2devops:<password>@r2devops-postgresql:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4"
      
    2. Add GitLab OIDC configuration

      Todo

      • Replace REPLACE_ME_BY_APPLICATION_ID by Application ID get in GitLab OIDC step above
      • Replace REPLACE_ME_BY_APPLICATION_SECRET by Secret get in GitLab OIDC step above
      • Replace gitlab.mydomain.com by the domain of your GitLab instance
            selfservice:
              methods:
                oidc:
                  config:
                    providers:
                      - id: gitlab
                        provider: gitlab
                        mapper_url: file:///etc/config/oidc.gitlab.jsonnet
                        scope:
                          - openid
                          - email
                        client_id: "REPLACE_ME_BY_APPLICATION_ID"
                        client_secret: "REPLACE_ME_BY_APPLICATION_SECRET"
                        issuer_url: "https://gitlab.mydomain.com"
      
    3. Add domain configuration

      Todo

      • Replace all occurrences of mydomain.com by your domain
              default_browser_return_url: "https://r2devops.mydomain.com/u/signin"
              whitelisted_return_urls:
                - "https://r2devops.mydomain.com"
              flows:
                error:
                  ui_url: "https://r2devops.mydomain.com"
                login:
                  ui_url: "https://r2devops.mydomain.com/u/signin"
                logout:
                  after:
                    default_browser_return_url: "https://r2devops.mydomain.com"
                settings:
                  ui_url: "https://r2devops.mydomain.com/u/dashboard/profile"
                registration:
                  ui_url: "https://r2devops.mydomain.com/u/signup"
                recovery:
                  ui_url: "https://r2devops.mydomain.com/u/recovery"
            serve:
              public:
                base_url: "https://api.r2devops.mydomain.com/kratos/public"
                cors:
                  allowed_origins:
                    - "https://r2devops.mydomain.com*"
            cookies:
              domain: "r2devops.mydomain.com"
      
    4. Add secrets configuration

      Todo

      • Replace all occurrences of REPLACE_ME_BY_NEW_SECRET by a new secret of 32 alphanumeric char
            secrets:
              default:
                - "REPLACE_ME_BY_NEW_SECRET"
              cookie:
                - "REPLACE_ME_BY_NEW_SECRET"
              cipher:
                - "REPLACE_ME_BY_NEW_SECRET"
      
    🔐 With existing secret

    Todo

    • Replace all occurrences of mydomain.com by your domain
      deployment:
        extraEnv:
          - name: SELFSERVICE_METHODS_OIDC_CONFIG_PROVIDERS
            valueFrom:
              secretKeyRef:
                name: "kratos-secret"
                key: "oidc"
      secret:
        enabled: false # We disable creation of Kratos chart secret to use our own
        nameOverride: "kratos-secret"
    
      kratos:
        config:
          selfservice:
            default_browser_return_url: "https://r2devops.mydomain.com/u/signin"
            whitelisted_return_urls:
              - "https://r2devops.mydomain.com"
            flows:
              error:
                ui_url: "https://r2devops.mydomain.com"
              login:
                ui_url: "https://r2devops.mydomain.com/u/signin"
              logout:
                after:
                  default_browser_return_url: "https://r2devops.mydomain.com"
              settings:
                ui_url: "https://r2devops.mydomain.com/u/dashboard/profile"
              registration:
                ui_url: "https://r2devops.mydomain.com/u/signup"
              recovery:
                ui_url: "https://r2devops.mydomain.com/u/recovery"
          serve:
            public:
              base_url: "https://api.r2devops.mydomain.com/kratos/public"
              cors:
                allowed_origins:
                  - "https://r2devops.mydomain.com*"
          cookies:
            domain: "r2devops.mydomain.com"
    

🚀 Install the chart

  1. For the first installation, the chart must be installed with kratos dependency disabled
    helm upgrade -n $R2DEVOPS_NS --install r2devops r2devops/r2devops --set kratos.dependency.enabled=false -f custom_values.yaml
    
  2. Then, upgrade the chart without kratos dependency disabled
    helm upgrade -n $R2DEVOPS_NS --install r2devops r2devops/r2devops -f custom_values.yaml
    

Congratulations

You have successfully installed R2Devops on your Kubernetes cluster 🎉

What's next

Now that you have finished this tutorial, here are some simple tasks you should give a try :

  • 📈 Learn how to use the platform by reading the documentation
  • 📕 Import your first job, here is the tutorial

Not the same behavior

Did you encounter a problem during the installation process ? See the troubleshooting section.

📚 Configuration example

Info

Theffollowing examples run in a Kubernetes cluster using nginx as ingressController, cert-manager and a clusterIssuer named letsencrypt-production"

📦 No external services

This is an example of custom values.yaml file using all services from chart dependencies.

Example (to expand)
front:
  host: "r2devops.mydomain.com"

jobs:
  host: "api.r2devops.mydomain.com"
  extraEnv:
    - name: PROJECTS_LIMIT_KEY
      value: REDACTED

gitlab:
  domain: "https://gitlab.mydomain.com"

worker:
  replicaCount: 15

ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-production"

# ref. https://github.com/minio/minio/blob/master/helm/minio/values.yaml
minio:
  dependency:
    enabled: true
  auth:
    rootPassword: REDACTED
  image:
    debug: true

# ref. https://github.com/bitnami/charts/blob/main/bitnami/postgresql/values.yaml
postgresql:
  dependency:
    enabled: true
  global:
    postgresql:
      auth:
        password: REDACTED
        postgresPassword: REDACTED

# ref. https://github.com/bitnami/charts/blob/main/bitnami/redis/values.yaml
redis:
  dependency:
    enabled: true
  auth:
    password: REDACTED

# ref. https://github.com/ory/k8s/blob/master/helm/charts/kratos/values.yaml
kratos:
  dependency:
    enabled: true
  ingress:
    public:
      annotations:
        cert-manager.io/cluster-issuer: "letsencrypt-production"
        ingress.kubernetes.io/ssl-redirect: "true"
        nginx.ingress.kubernetes.io/rewrite-target: /$2
      className: "nginx"
      hosts:
        - host: "api.r2devops.mydomain.com"
          paths:
            - path: /kratos/public(/|$)(.*)
              pathType: ImplementationSpecific
      tls:
        - secretName: r2devops-jobs-tls # This must be the same name that api
          hosts:
            - "api.r2devops.mydomain.com"

  kratos:
    config:
      courier:
        # Not used but mandatory
        smtp:
          from_name: R2DevOps
          from_address: tech@r2devops.io
          connection_uri: "smtp://mailhog:1025"
      dsn: "postgres://postgres:REDACTED@r2devops-postgresql:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4"
      serve:
        public:
          base_url: "https://api.r2devops.mydomain.com/kratos/public"
          cors:
            allowed_origins:
              - "https://r2devops.mydomain.com*"
      selfservice:
        methods:
          oidc:
            config:
              providers:
                - id: gitlab
                  provider: gitlab
                  mapper_url: file:///etc/config/oidc.gitlab.jsonnet
                  scope:
                    - openid
                    - email
                  client_id: "REDACTED"
                  client_secret: "REDACTED"
                  issuer_url: "https://gitlab.mydomain.com"
        default_browser_return_url: "https://r2devops.mydomain.com/u/signin"
        whitelisted_return_urls:
          - "https://r2devops.mydomain.com"
        flows:
          error:
            ui_url: "https://r2devops.mydomain.com"
          login:
            ui_url: "https://r2devops.mydomain.com/u/signin"
          logout:
            after:
              default_browser_return_url: "https://r2devops.mydomain.com"
          settings:
            ui_url: "https://r2devops.mydomain.com/u/dashboard/profile"
          registration:
            ui_url: "https://r2devops.mydomain.com/u/signup"
          recovery:
            ui_url: "https://r2devops.mydomain.com/u/recovery"
      cookies:
        domain: "r2devops.mydomain.com"
      secrets:
        default:
          - "REDACTED" # 32 char alaphanumeric
        cookie:
          - "REDACTED" # 32 char alaphanumeric
        cipher:
          - "REDACTED" # 32 char alaphanumeric

📥 External services

This is an example of custom values.yaml file using external services for MinIO, PostgreSQL and Redis.

Example (to expand)
front:
  host: "r2devops.mydomain.com"

jobs:
  host: "api.r2devops.mydomain.com"
  extraEnv:
    - name: PROJECTS_LIMIT_KEY
      value: REDACTED

gitlab:
  domain: "https://gitlab.mydomain.com"

worker:
  replicaCount: 20

ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-production"

# ref. https://github.com/minio/minio/blob/master/helm/minio/values.yaml
minio:
  dependency:
    enabled: false
  auth:
    rootUser: REDACTED
    rootPassword: REDACTED
  custom:
    host: "https://s3.us-east-1.amazonaws.com"
    bucketName: "r2devops-files"
    region: "us-east-1"
    disableSSL: "false"
    forcePathStyle: "false"

# ref. https://github.com/bitnami/charts/blob/main/bitnami/postgresql/values.yaml
postgresql:
  dependency:
    enabled: false
  global:
    postgresql:
      auth:
        username: REDACTED
        postgresPassword: REDACTED
  custom:
    host: "database-1.REDACTED.us-east-1.rds.amazonaws.com"
    port: 5432
    dbName: "r2devops"
    sslmode: "require"

# ref. https://github.com/bitnami/charts/blob/main/bitnami/redis/values.yaml
redis:
  dependency:
    enabled: false
  auth:
    password: REDACTED
  custom:
    port: 6379
    host: "REDACTED"
    user: "REDACTED"
    cert: |
      -----BEGIN CERTIFICATE-----
      REDACTED
      -----END CERTIFICATE-----

# ref. https://github.com/ory/k8s/blob/master/helm/charts/kratos/values.yaml
kratos:
  dependency:
    enabled: true
  ingress:
    public:
      annotations:
        cert-manager.io/cluster-issuer: "letsencrypt-production"
        ingress.kubernetes.io/ssl-redirect: "true"
        nginx.ingress.kubernetes.io/rewrite-target: /$2
      className: "nginx"
      hosts:
        - host: "api.r2devops.mydomain.com"
          paths:
            - path: /kratos/public(/|$)(.*)
              pathType: ImplementationSpecific
      tls:
        - secretName: r2devops-jobs-tls # This must be the same name that api
          hosts:
            - "api.r2devops.mydomain.com"

  kratos:
    config:
      dsn: "postgres://postgres:REDACTED@database-1.REDACTED.us-east-1.rds.amazonaws.com:5432/kratos?sslmode=require&max_conns=20&max_idle_conns=4"
      serve:
        public:
          base_url: "https://api.r2devops.mydomain.com/kratos/public"
          cors:
            allowed_origins:
              - "https://r2devops.mydomain.com*"
      selfservice:
        methods:
          oidc:
            config:
              providers:
                - id: gitlab
                  provider: gitlab
                  mapper_url: file:///etc/config/oidc.gitlab.jsonnet
                  scope:
                    - openid
                    - email
                  client_id: "REDACTED"
                  client_secret: "REDACTED"
                  issuer_url: "https://gitlab.mydomain.com"
        default_browser_return_url: "https://r2devops.mydomain.com/u/signin"
        whitelisted_return_urls:
          - "https://r2devops.mydomain.com"
        flows:
          error:
            ui_url: "https://r2devops.mydomain.com"
          login:
            ui_url: "https://r2devops.mydomain.com/u/signin"
          logout:
            after:
              default_browser_return_url: "https://r2devops.mydomain.com"
          settings:
            ui_url: "https://r2devops.mydomain.com/u/dashboard/profile"
          registration:
            ui_url: "https://r2devops.mydomain.com/u/signup"
          recovery:
            ui_url: "https://r2devops.mydomain.com/u/recovery"
      cookies:
        domain: "r2devops.mydomain.com"
      secrets:
        default:
          - "REDACTED" # 32 char alphanumeric
        cookie:
          - "REDACTED" # 32 char alphanumeric
        cipher:
          - "REDACTED" # 32 char alphanumeric