---
title: "Terraform + k8s"
description: "Deploy Bifrost as a service in Kubernetes clusters across AWS, Azure, and GCP using Terraform"
icon: "cloud"
---

Deploy Bifrost on Kubernetes using Terraform. This guide breaks down the deployment into individual components for better understanding.

<Note>
Bifrost also provides a ready-to-use Terraform module that handles all the infrastructure setup for you.
You can use it directly from GitHub:
```hcl
module "bifrost" {
  source         = "github.com/maximhq/bifrost//terraform/modules/bifrost?ref=terraform/v0.1.0"
  cloud_provider = "aws"          # "aws" | "gcp" | "azure" | "kubernetes"
  service        = "eks"          # AWS: "ecs" | "eks", GCP: "gke" | "cloud-run", Azure: "aks" | "aci", K8s: "deployment"
  region         = "us-east-1"
  image_tag      = "latest"
}
```
See the [Terraform module README](https://github.com/maximhq/bifrost/tree/main/terraform) for full documentation and examples.
</Note>

<Note>
If you are using Postgres/MySQL for config and log store, you can skip the Volume configuration and permission changes sections.
</Note>

<Note>
If you use PostgreSQL for `config_store` or `logs_store`, ensure the target database is UTF8 encoded. See [PostgreSQL UTF8 Requirement](../quickstart/gateway/setting-up#postgresql-utf8-requirement).
</Note>

<Tabs>
<Tab title="AWS">

## 1. Volume Configuration

Create an EBS volume, persistent volume, and persistent volume claim for Bifrost data storage.

```terraform
locals {
  service_name = "bifrost-service"
}

resource "aws_ebs_volume" "bifrost_disk" {
  availability_zone = "${var.region}${var.main_zone}"
  size              = var.volume_size_gb
  type              = "gp3"
  encrypted         = true

  tags = {
    Name = "bifrost-disk"
  }

  lifecycle {
    ignore_changes = [tags]
  }
}

resource "kubernetes_persistent_volume" "bifrost_volume" {
  metadata {
    name = "bifrost-volume"
  }
  spec {
    capacity = {
      storage = "${var.volume_size_gb}Gi"
    }
    access_modes                     = ["ReadWriteOnce"]
    persistent_volume_reclaim_policy = "Retain"
    storage_class_name               = "gp3"
    persistent_volume_source {
      aws_elastic_block_store {
        volume_id = aws_ebs_volume.bifrost_disk.id
        fs_type   = "ext4"
      }
    }
  }
  depends_on = [aws_ebs_volume.bifrost_disk]

  lifecycle {
    prevent_destroy = false
  }
}

resource "kubernetes_persistent_volume_claim" "bifrost_volume_claim" {
  metadata {
    name      = "bifrost-volume-claim"
    namespace = var.namespace
  }
  spec {
    access_modes = ["ReadWriteOnce"]
    resources {
      requests = {
        storage = "${var.volume_size_gb}Gi"
      }
    }
    storage_class_name = "gp3"
    volume_name        = "bifrost-volume"
  }
  depends_on = [kubernetes_persistent_volume.bifrost_volume]
}
```

## 2. Configuration Secret

Create a Kubernetes secret to store Bifrost configuration with Postgres backend.

<Note>
This configuration uses Postgres for both config store and logs store. The secret is mounted as a file at `/app/data/config.json` in the container.
</Note>

```terraform
resource "kubernetes_secret" "bifrost_config" {
  metadata {
    name      = "bifrost-config"
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
  }

  data = {
    "config.json" = jsonencode({
      "config_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      },
      "logs_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      }
    })
  }

  type = "Opaque"
  depends_on = [kubernetes_namespace.bifrost_namespace]
}
```

## 3. Deployment Configuration

Create the Bifrost deployment with proper security contexts and volume mounts.

<Note>
**Volume Permissions**: The deployment includes an init container that sets proper ownership (1000:1000) and permissions (755) on the mounted volume. This ensures the Bifrost container can read/write to the volume.
- `fs_group: 1000` sets the volume's group ownership
- `run_as_user: 1000` runs the container as non-root user
- Init container runs as root to fix permissions before the main container starts
</Note>

```terraform
resource "kubernetes_deployment" "bifrost_deployment" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
      env = var.env
    }
  }

  spec {
    replicas = var.replica_count

    selector {
      match_labels = {
        app = local.service_name
      }
    }

    template {
      metadata {
        labels = {
          app = local.service_name
          env = var.env
        }
      }

      spec {
        security_context {
          fs_group               = 1000
          fs_group_change_policy = "OnRootMismatch"
        }

        init_container {
          name    = "fix-permissions"
          image   = "busybox:latest"
          command = ["sh", "-c", "chown -R 1000:1000 /app/data && chmod -R 755 /app/data"]

          security_context {
            run_as_user = 0
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }
        }

        container {
          name  = "bifrost-service"
          image = "maximhq/bifrost:${var.image_tag}"

          port {
            container_port = 8080
            name           = "http"
          }

          security_context {
            run_as_user                = 1000
            run_as_group               = 1000
            run_as_non_root            = true
            allow_privilege_escalation = false
          }

          resources {
            requests = {
              cpu    = "250m"
              memory = "512Mi"
            }
            limits = {
              cpu    = "500m"
              memory = "1Gi"
            }
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }

          volume_mount {
            name       = "config-volume"
            mount_path = "/app/data/config.json"
            sub_path   = "config.json"
          }

          liveness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 30
            period_seconds        = 10
            timeout_seconds       = 5
            failure_threshold     = 3
          }

          readiness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 10
            period_seconds        = 5
            timeout_seconds       = 3
            failure_threshold     = 3
          }
        }

        volume {
          name = "bifrost-volume"
          persistent_volume_claim {
            claim_name = "bifrost-volume-claim"
          }
        }

        volume {
          name = "config-volume"
          secret {
            secret_name = kubernetes_secret.bifrost_config.metadata[0].name
          }
        }
      }
    }
  }
  depends_on = [kubernetes_secret.bifrost_config, kubernetes_persistent_volume_claim.bifrost_volume_claim]
}
```

## 4. Service Configuration

Create a Kubernetes service to expose the Bifrost deployment.

```terraform
resource "kubernetes_service" "bifrost_service" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
    }
  }

  spec {
    selector = {
      app = local.service_name
    }

    port {
      name        = "http"
      port        = 80
      target_port = 8080
      protocol    = "TCP"
    }

    type = "ClusterIP"
  }
}
```

## Complete Configuration

Here's the complete Terraform configuration combining all components:

```terraform
locals {
  service_name = "bifrost-service"
}

# Volume Configuration
resource "aws_ebs_volume" "bifrost_disk" {
  availability_zone = "${var.region}${var.main_zone}"
  size              = var.volume_size_gb
  type              = "gp3"
  encrypted         = true

  tags = {
    Name = "bifrost-disk"
  }

  lifecycle {
    ignore_changes = [tags]
  }
}

resource "kubernetes_persistent_volume" "bifrost_volume" {
  metadata {
    name = "bifrost-volume"
  }
  spec {
    capacity = {
      storage = "${var.volume_size_gb}Gi"
    }
    access_modes                     = ["ReadWriteOnce"]
    persistent_volume_reclaim_policy = "Retain"
    storage_class_name               = "gp3"
    persistent_volume_source {
      aws_elastic_block_store {
        volume_id = aws_ebs_volume.bifrost_disk.id
        fs_type   = "ext4"
      }
    }
  }
  depends_on = [aws_ebs_volume.bifrost_disk]

  lifecycle {
    prevent_destroy = false
  }
}

resource "kubernetes_persistent_volume_claim" "bifrost_volume_claim" {
  metadata {
    name      = "bifrost-volume-claim"
    namespace = var.namespace
  }
  spec {
    access_modes = ["ReadWriteOnce"]
    resources {
      requests = {
        storage = "${var.volume_size_gb}Gi"
      }
    }
    storage_class_name = "gp3"
    volume_name        = "bifrost-volume"
  }
  depends_on = [kubernetes_persistent_volume.bifrost_volume]
}

# Configuration Secret
resource "kubernetes_secret" "bifrost_config" {
  metadata {
    name      = "bifrost-config"
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
  }

  data = {
    "config.json" = jsonencode({
      "config_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      },
      "logs_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      }
    })
  }

  type = "Opaque"
  depends_on = [kubernetes_namespace.bifrost_namespace]
}

# Deployment Configuration
resource "kubernetes_deployment" "bifrost_deployment" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
      env = var.env
    }
  }

  spec {
    replicas = var.replica_count

    selector {
      match_labels = {
        app = local.service_name
      }
    }

    template {
      metadata {
        labels = {
          app = local.service_name
          env = var.env
        }
      }

      spec {
        security_context {
          fs_group               = 1000
          fs_group_change_policy = "OnRootMismatch"
        }

        init_container {
          name    = "fix-permissions"
          image   = "busybox:latest"
          command = ["sh", "-c", "chown -R 1000:1000 /app/data && chmod -R 755 /app/data"]

          security_context {
            run_as_user = 0
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }
        }

        container {
          name  = "bifrost-service"
          image = "maximhq/bifrost:${var.image_tag}"

          port {
            container_port = 8080
            name           = "http"
          }

          security_context {
            run_as_user                = 1000
            run_as_group               = 1000
            run_as_non_root            = true
            allow_privilege_escalation = false
          }

          resources {
            requests = {
              cpu    = "250m"
              memory = "512Mi"
            }
            limits = {
              cpu    = "500m"
              memory = "1Gi"
            }
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }

          volume_mount {
            name       = "config-volume"
            mount_path = "/app/data/config.json"
            sub_path   = "config.json"
          }

          liveness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 30
            period_seconds        = 10
            timeout_seconds       = 5
            failure_threshold     = 3
          }

          readiness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 10
            period_seconds        = 5
            timeout_seconds       = 3
            failure_threshold     = 3
          }
        }

        volume {
          name = "bifrost-volume"
          persistent_volume_claim {
            claim_name = "bifrost-volume-claim"
          }
        }

        volume {
          name = "config-volume"
          secret {
            secret_name = kubernetes_secret.bifrost_config.metadata[0].name
          }
        }
      }
    }
  }
  depends_on = [kubernetes_secret.bifrost_config, kubernetes_persistent_volume_claim.bifrost_volume_claim]
}

# Service Configuration
resource "kubernetes_service" "bifrost_service" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
    }
  }

  spec {
    selector = {
      app = local.service_name
    }

    port {
      name        = "http"
      port        = 80
      target_port = 8080
      protocol    = "TCP"
    }

    type = "ClusterIP"
  }
}
```

</Tab>

<Tab title="Azure">

## 1. Volume Configuration

Create an Azure managed disk, persistent volume, and persistent volume claim for Bifrost data storage.

```terraform
locals {
  service_name = "bifrost-service"
}

resource "azurerm_managed_disk" "bifrost_disk" {
  name                 = "bifrost-disk"
  location             = var.region
  resource_group_name  = var.resource_group_name
  storage_account_type = "Premium_LRS"
  create_option        = "Empty"
  disk_size_gb         = var.volume_size_gb

  lifecycle {
    ignore_changes = [tags]
  }
}

resource "kubernetes_persistent_volume" "bifrost_volume" {
  metadata {
    name = "bifrost-volume"
  }
  spec {
    capacity = {
      storage = "${var.volume_size_gb}Gi"
    }
    access_modes                     = ["ReadWriteOnce"]
    persistent_volume_reclaim_policy = "Retain"
    storage_class_name               = "managed-premium"
    persistent_volume_source {
      azure_disk {
        disk_name     = azurerm_managed_disk.bifrost_disk.name
        data_disk_uri = azurerm_managed_disk.bifrost_disk.id
        kind          = "Managed"
      }
    }
  }
  depends_on = [azurerm_managed_disk.bifrost_disk]

  lifecycle {
    prevent_destroy = false
  }
}

resource "kubernetes_persistent_volume_claim" "bifrost_volume_claim" {
  metadata {
    name      = "bifrost-volume-claim"
    namespace = var.namespace
  }
  spec {
    access_modes = ["ReadWriteOnce"]
    resources {
      requests = {
        storage = "${var.volume_size_gb}Gi"
      }
    }
    storage_class_name = "managed-premium"
    volume_name        = "bifrost-volume"
  }
  depends_on = [kubernetes_persistent_volume.bifrost_volume]
}
```

## 2. Configuration Secret

Create a Kubernetes secret to store Bifrost configuration with Postgres backend.

<Note>
This configuration uses Postgres for both config store and logs store. The secret is mounted as a file at `/app/data/config.json` in the container.
</Note>

```terraform
resource "kubernetes_secret" "bifrost_config" {
  metadata {
    name      = "bifrost-config"
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
  }

  data = {
    "config.json" = jsonencode({
      "config_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      },
      "logs_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      }
    })
  }

  type = "Opaque"
  depends_on = [kubernetes_namespace.bifrost_namespace]
}
```

## 3. Deployment Configuration

Create the Bifrost deployment with proper security contexts and volume mounts.

<Note>
**Volume Permissions**: The deployment includes an init container that sets proper ownership (1000:1000) and permissions (755) on the mounted volume. This ensures the Bifrost container can read/write to the volume.
- `fs_group: 1000` sets the volume's group ownership
- `run_as_user: 1000` runs the container as non-root user
- Init container runs as root to fix permissions before the main container starts
</Note>

```terraform
resource "kubernetes_deployment" "bifrost_deployment" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
      env = var.env
    }
  }

  spec {
    replicas = var.replica_count

    selector {
      match_labels = {
        app = local.service_name
      }
    }

    template {
      metadata {
        labels = {
          app = local.service_name
          env = var.env
        }
      }

      spec {
        security_context {
          fs_group               = 1000
          fs_group_change_policy = "OnRootMismatch"
        }

        init_container {
          name    = "fix-permissions"
          image   = "busybox:latest"
          command = ["sh", "-c", "chown -R 1000:1000 /app/data && chmod -R 755 /app/data"]

          security_context {
            run_as_user = 0
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }
        }

        container {
          name  = "bifrost-service"
          image = "maximhq/bifrost:${var.image_tag}"

          port {
            container_port = 8080
            name           = "http"
          }

          security_context {
            run_as_user                = 1000
            run_as_group               = 1000
            run_as_non_root            = true
            allow_privilege_escalation = false
          }

          resources {
            requests = {
              cpu    = "250m"
              memory = "512Mi"
            }
            limits = {
              cpu    = "500m"
              memory = "1Gi"
            }
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }

          volume_mount {
            name       = "config-volume"
            mount_path = "/app/data/config.json"
            sub_path   = "config.json"
          }

          liveness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 30
            period_seconds        = 10
            timeout_seconds       = 5
            failure_threshold     = 3
          }

          readiness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 10
            period_seconds        = 5
            timeout_seconds       = 3
            failure_threshold     = 3
          }
        }

        volume {
          name = "bifrost-volume"
          persistent_volume_claim {
            claim_name = "bifrost-volume-claim"
          }
        }

        volume {
          name = "config-volume"
          secret {
            secret_name = kubernetes_secret.bifrost_config.metadata[0].name
          }
        }
      }
    }
  }
  depends_on = [kubernetes_secret.bifrost_config, kubernetes_persistent_volume_claim.bifrost_volume_claim]
}
```

## 4. Service Configuration

Create a Kubernetes service to expose the Bifrost deployment.

```terraform
resource "kubernetes_service" "bifrost_service" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
    }
  }

  spec {
    selector = {
      app = local.service_name
    }

    port {
      name        = "http"
      port        = 80
      target_port = 8080
      protocol    = "TCP"
    }

    type = "ClusterIP"
  }
}
```

## Complete Configuration

Here's the complete Terraform configuration combining all components:

```terraform
locals {
  service_name = "bifrost-service"
}

# Volume Configuration
resource "azurerm_managed_disk" "bifrost_disk" {
  name                 = "bifrost-disk"
  location             = var.region
  resource_group_name  = var.resource_group_name
  storage_account_type = "Premium_LRS"
  create_option        = "Empty"
  disk_size_gb         = var.volume_size_gb

  lifecycle {
    ignore_changes = [tags]
  }
}

resource "kubernetes_persistent_volume" "bifrost_volume" {
  metadata {
    name = "bifrost-volume"
  }
  spec {
    capacity = {
      storage = "${var.volume_size_gb}Gi"
    }
    access_modes                     = ["ReadWriteOnce"]
    persistent_volume_reclaim_policy = "Retain"
    storage_class_name               = "managed-premium"
    persistent_volume_source {
      azure_disk {
        disk_name     = azurerm_managed_disk.bifrost_disk.name
        data_disk_uri = azurerm_managed_disk.bifrost_disk.id
        kind          = "Managed"
      }
    }
  }
  depends_on = [azurerm_managed_disk.bifrost_disk]

  lifecycle {
    prevent_destroy = false
  }
}

resource "kubernetes_persistent_volume_claim" "bifrost_volume_claim" {
  metadata {
    name      = "bifrost-volume-claim"
    namespace = var.namespace
  }
  spec {
    access_modes = ["ReadWriteOnce"]
    resources {
      requests = {
        storage = "${var.volume_size_gb}Gi"
      }
    }
    storage_class_name = "managed-premium"
    volume_name        = "bifrost-volume"
  }
  depends_on = [kubernetes_persistent_volume.bifrost_volume]
}

# Configuration Secret
resource "kubernetes_secret" "bifrost_config" {
  metadata {
    name      = "bifrost-config"
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
  }

  data = {
    "config.json" = jsonencode({
      "config_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      },
      "logs_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      }
    })
  }

  type = "Opaque"
  depends_on = [kubernetes_namespace.bifrost_namespace]
}

# Deployment Configuration
resource "kubernetes_deployment" "bifrost_deployment" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
      env = var.env
    }
  }

  spec {
    replicas = var.replica_count

    selector {
      match_labels = {
        app = local.service_name
      }
    }

    template {
      metadata {
        labels = {
          app = local.service_name
          env = var.env
        }
      }

      spec {
        security_context {
          fs_group               = 1000
          fs_group_change_policy = "OnRootMismatch"
        }

        init_container {
          name    = "fix-permissions"
          image   = "busybox:latest"
          command = ["sh", "-c", "chown -R 1000:1000 /app/data && chmod -R 755 /app/data"]

          security_context {
            run_as_user = 0
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }
        }

        container {
          name  = "bifrost-service"
          image = "maximhq/bifrost:${var.image_tag}"

          port {
            container_port = 8080
            name           = "http"
          }

          security_context {
            run_as_user                = 1000
            run_as_group               = 1000
            run_as_non_root            = true
            allow_privilege_escalation = false
          }

          resources {
            requests = {
              cpu    = "250m"
              memory = "512Mi"
            }
            limits = {
              cpu    = "500m"
              memory = "1Gi"
            }
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }

          volume_mount {
            name       = "config-volume"
            mount_path = "/app/data/config.json"
            sub_path   = "config.json"
          }

          liveness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 30
            period_seconds        = 10
            timeout_seconds       = 5
            failure_threshold     = 3
          }

          readiness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 10
            period_seconds        = 5
            timeout_seconds       = 3
            failure_threshold     = 3
          }
        }

        volume {
          name = "bifrost-volume"
          persistent_volume_claim {
            claim_name = "bifrost-volume-claim"
          }
        }

        volume {
          name = "config-volume"
          secret {
            secret_name = kubernetes_secret.bifrost_config.metadata[0].name
          }
        }
      }
    }
  }
  depends_on = [kubernetes_secret.bifrost_config, kubernetes_persistent_volume_claim.bifrost_volume_claim]
}

# Service Configuration
resource "kubernetes_service" "bifrost_service" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
    }
  }

  spec {
    selector = {
      app = local.service_name
    }

    port {
      name        = "http"
      port        = 80
      target_port = 8080
      protocol    = "TCP"
    }

    type = "ClusterIP"
  }
}
```

</Tab>

<Tab title="GCP">

## 1. Volume Configuration

Create a GCP persistent disk, persistent volume, and persistent volume claim for Bifrost data storage.

```terraform
locals {
  service_name = "bifrost-service"
}

resource "google_compute_disk" "bifrost_disk" {
  name = "bifrost-disk"
  size = var.volume_size_gb
  type = "pd-ssd"
  zone = "${var.region}-${var.main_zone}"

  lifecycle {
    ignore_changes = [labels]
  }
}

resource "kubernetes_persistent_volume" "bifrost_volume" {
  metadata {
    name = "bifrost-volume"
  }
  spec {
    capacity = {
      storage = "${var.volume_size_gb}Gi"
    }
    access_modes                     = ["ReadWriteOnce"]
    persistent_volume_reclaim_policy = "Retain"
    storage_class_name               = "premium-rwo"
    persistent_volume_source {
      gce_persistent_disk {
        pd_name = "bifrost-disk"
      }
    }
  }
  depends_on = [google_compute_disk.bifrost_disk]

  lifecycle {
    prevent_destroy = false
  }
}

resource "kubernetes_persistent_volume_claim" "bifrost_volume_claim" {
  metadata {
    name      = "bifrost-volume-claim"
    namespace = var.namespace
  }
  spec {
    access_modes = ["ReadWriteOnce"]
    resources {
      requests = {
        storage = "${var.volume_size_gb}Gi"
      }
    }
    storage_class_name = "premium-rwo"
    volume_name        = "bifrost-volume"
  }
  depends_on = [kubernetes_persistent_volume.bifrost_volume]
}
```

## 2. Configuration Secret

Create a Kubernetes secret to store Bifrost configuration with Postgres backend.

<Note>
This configuration uses Postgres for both config store and logs store. The secret is mounted as a file at `/app/data/config.json` in the container.
</Note>

```terraform
resource "kubernetes_secret" "bifrost_config" {
  metadata {
    name      = "bifrost-config"
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
  }

  data = {
    "config.json" = jsonencode({
      "config_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      },
      "logs_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      }
    })
  }

  type = "Opaque"
  depends_on = [kubernetes_namespace.bifrost_namespace]
}
```

## 3. Deployment Configuration

Create the Bifrost deployment with proper security contexts and volume mounts.

<Note>
**Volume Permissions**: The deployment includes an init container that sets proper ownership (1000:1000) and permissions (755) on the mounted volume. This ensures the Bifrost container can read/write to the volume.
- `fs_group: 1000` sets the volume's group ownership
- `run_as_user: 1000` runs the container as non-root user
- Init container runs as root to fix permissions before the main container starts
</Note>

```terraform
resource "kubernetes_deployment" "bifrost_deployment" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
      env = var.env
    }
  }

  spec {
    replicas = var.replica_count

    selector {
      match_labels = {
        app = local.service_name
      }
    }

    template {
      metadata {
        labels = {
          app = local.service_name
          env = var.env
        }
      }

      spec {
        security_context {
          fs_group               = 1000
          fs_group_change_policy = "OnRootMismatch"
        }

        init_container {
          name    = "fix-permissions"
          image   = "busybox:latest"
          command = ["sh", "-c", "chown -R 1000:1000 /app/data && chmod -R 755 /app/data"]

          security_context {
            run_as_user = 0
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }
        }

        container {
          name  = "bifrost-service"
          image = "maximhq/bifrost:${var.image_tag}"

          port {
            container_port = 8080
            name           = "http"
          }

          security_context {
            run_as_user                = 1000
            run_as_group               = 1000
            run_as_non_root            = true
            allow_privilege_escalation = false
          }

          resources {
            requests = {
              cpu    = "250m"
              memory = "512Mi"
            }
            limits = {
              cpu    = "500m"
              memory = "1Gi"
            }
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }

          volume_mount {
            name       = "config-volume"
            mount_path = "/app/data/config.json"
            sub_path   = "config.json"
          }

          liveness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 30
            period_seconds        = 10
            timeout_seconds       = 5
            failure_threshold     = 3
          }

          readiness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 10
            period_seconds        = 5
            timeout_seconds       = 3
            failure_threshold     = 3
          }
        }

        volume {
          name = "bifrost-volume"
          persistent_volume_claim {
            claim_name = "bifrost-volume-claim"
          }
        }

        volume {
          name = "config-volume"
          secret {
            secret_name = kubernetes_secret.bifrost_config.metadata[0].name
          }
        }
      }
    }
  }
  depends_on = [kubernetes_secret.bifrost_config, kubernetes_persistent_volume_claim.bifrost_volume_claim]
}
```

## 4. Service Configuration

Create a Kubernetes service to expose the Bifrost deployment.

```terraform
resource "kubernetes_service" "bifrost_service" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
    }
  }

  spec {
    selector = {
      app = local.service_name
    }

    port {
      name        = "http"
      port        = 80
      target_port = 8080
      protocol    = "TCP"
    }

    type = "ClusterIP"
  }
}
```

## Complete Configuration

Here's the complete Terraform configuration combining all components:

```terraform
locals {
  service_name = "bifrost-service"
}

# Volume Configuration
resource "google_compute_disk" "bifrost_disk" {
  name = "bifrost-disk"
  size = var.volume_size_gb
  type = "pd-ssd"
  zone = "${var.region}-${var.main_zone}"

  lifecycle {
    ignore_changes = [labels]
  }
}

resource "kubernetes_persistent_volume" "bifrost_volume" {
  metadata {
    name = "bifrost-volume"
  }
  spec {
    capacity = {
      storage = "${var.volume_size_gb}Gi"
    }
    access_modes                     = ["ReadWriteOnce"]
    persistent_volume_reclaim_policy = "Retain"
    storage_class_name               = "premium-rwo"
    persistent_volume_source {
      gce_persistent_disk {
        pd_name = "bifrost-disk"
      }
    }
  }
  depends_on = [google_compute_disk.bifrost_disk]

  lifecycle {
    prevent_destroy = false
  }
}

resource "kubernetes_persistent_volume_claim" "bifrost_volume_claim" {
  metadata {
    name      = "bifrost-volume-claim"
    namespace = var.namespace
  }
  spec {
    access_modes = ["ReadWriteOnce"]
    resources {
      requests = {
        storage = "${var.volume_size_gb}Gi"
      }
    }
    storage_class_name = "premium-rwo"
    volume_name        = "bifrost-volume"
  }
  depends_on = [kubernetes_persistent_volume.bifrost_volume]
}

# Configuration Secret
resource "kubernetes_secret" "bifrost_config" {
  metadata {
    name      = "bifrost-config"
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
  }

  data = {
    "config.json" = jsonencode({
      "config_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      },
      "logs_store" : {
        "enabled" : true,
        "type" : "postgres",
        "config" : {
          "host" : "${var.pg_host}",
          "port" : "${var.pg_port}",
          "user" : "${var.pg_user}",
          "password" : "${var.pg_password}",
          "db_name" : "${var.pg_database}",
          "ssl_mode": "disable"
        }
      }
    })
  }

  type = "Opaque"
  depends_on = [kubernetes_namespace.bifrost_namespace]
}

# Deployment Configuration
resource "kubernetes_deployment" "bifrost_deployment" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
      env = var.env
    }
  }

  spec {
    replicas = var.replica_count

    selector {
      match_labels = {
        app = local.service_name
      }
    }

    template {
      metadata {
        labels = {
          app = local.service_name
          env = var.env
        }
      }

      spec {
        security_context {
          fs_group               = 1000
          fs_group_change_policy = "OnRootMismatch"
        }

        init_container {
          name    = "fix-permissions"
          image   = "busybox:latest"
          command = ["sh", "-c", "chown -R 1000:1000 /app/data && chmod -R 755 /app/data"]

          security_context {
            run_as_user = 0
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }
        }

        container {
          name  = "bifrost-service"
          image = "maximhq/bifrost:${var.image_tag}"

          port {
            container_port = 8080
            name           = "http"
          }

          security_context {
            run_as_user                = 1000
            run_as_group               = 1000
            run_as_non_root            = true
            allow_privilege_escalation = false
          }

          resources {
            requests = {
              cpu    = "250m"
              memory = "512Mi"
            }
            limits = {
              cpu    = "500m"
              memory = "1Gi"
            }
          }

          volume_mount {
            name       = "bifrost-volume"
            mount_path = "/app/data"
          }

          volume_mount {
            name       = "config-volume"
            mount_path = "/app/data/config.json"
            sub_path   = "config.json"
          }

          liveness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 30
            period_seconds        = 10
            timeout_seconds       = 5
            failure_threshold     = 3
          }

          readiness_probe {
            http_get {
              path = "/health"
              port = 8080
            }
            initial_delay_seconds = 10
            period_seconds        = 5
            timeout_seconds       = 3
            failure_threshold     = 3
          }
        }

        volume {
          name = "bifrost-volume"
          persistent_volume_claim {
            claim_name = "bifrost-volume-claim"
          }
        }

        volume {
          name = "config-volume"
          secret {
            secret_name = kubernetes_secret.bifrost_config.metadata[0].name
          }
        }
      }
    }
  }
  depends_on = [kubernetes_secret.bifrost_config, kubernetes_persistent_volume_claim.bifrost_volume_claim]
}

# Service Configuration
resource "kubernetes_service" "bifrost_service" {
  metadata {
    name      = local.service_name
    namespace = kubernetes_namespace.bifrost_namespace.metadata[0].name
    labels = {
      app = local.service_name
    }
  }

  spec {
    selector = {
      app = local.service_name
    }

    port {
      name        = "http"
      port        = 80
      target_port = 8080
      protocol    = "TCP"
    }

    type = "ClusterIP"
  }
}
```

</Tab>
</Tabs>
