If you are using Postgres/MySQL for config and log store, you can skip the Volume configuration and permission changes sections.
- AWS
- Azure
- GCP
1. Volume Configuration
Create an EBS volume, persistent volume, and persistent volume claim for Bifrost data storage.Copy
Ask AI
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.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.Copy
Ask AI
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.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: 1000sets the volume’s group ownershiprun_as_user: 1000runs the container as non-root user- Init container runs as root to fix permissions before the main container starts
Copy
Ask AI
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.Copy
Ask AI
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:Copy
Ask AI
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"
}
}

