Deploy an HTTP Container with Terraform

Deploy an HTTP container on Bahriya with Terraform: public hostname with TLS, health checks, autoscaling, secrets, private registries and regions.

Updated 18 Sept 20265 min read

HTTP containers are long-running services that accept incoming web traffic. Bahriya provisions a public hostname with TLS, runs health checks, and autoscales your container across one or more regions.

Required fields

FieldTypeExampleDescription
handlestring"api"Permanent identifier. Used in hostnames.
namestring"API Server"Display name.
imagestring"nginx:alpine"Full image reference. A tag (or a digest) is required — an untagged image is rejected.
containerportstring"8080"The port your application listens on.
healthcheckpathstring"/healthz"HTTP path for health probes. Your app must respond 200 here, or the deploy will fail.
protocolstring"http"Protocol your app speaks on its port: http (default), https or tcp. Used both to health check it and to deliver traffic. Optional.
mincpustring"100"Guaranteed CPU in millicores.
minmemorystring"128"Guaranteed memory in megabytes.
autoscalingminreplicasstring"1"Minimum number of running instances.
activeregionslist["falkenstein-1"]One or more regions to deploy to.
projectstringbahriya_project.x.idThe project UUID (not handle).

Important: The containerport and healthcheckpath fields are critical. If your container does not respond with a 200 status at the configured port and path, the deploy will time out and the container will reach an error state.

Choosing the container protocol

protocol tells the platform what your app speaks on its port. It is used for both the health check and for delivering external traffic, so the two always agree — set it to https and your app is both probed over TLS and sent live traffic over TLS.

By default the platform sends an HTTP GET to healthcheckpath. Two other options cover apps that cannot answer that:

  • protocol = "https" — your app terminates TLS on its own port. The GET is sent over TLS, and the certificate is not checked, so a self-signed one is fine. External traffic is delivered over HTTPS too.
  • protocol = "tcp" — your app does not speak HTTP. The check only confirms the port accepts a connection, and healthcheckpath is ignored.
resource "bahriya_container" "bridge" {
  handle            = "queue-bridge"
  name              = "Queue Bridge"
  image             = "registry.example.com/team/bridge:1.4.2"
  containerport     = "9000"
  protocol          = "tcp"
  mincpu            = "50"
  minmemory         = "128"
  activeregions     = ["falkenstein-1"]
  project           = bahriya_project.app.id
}

Always tag your image

image must name a tag or a digest. nginx is rejected; nginx:1.25 and nginx@sha256:... are both accepted. An untagged reference cannot be deployed, and pinning a tag also means you can tell which build is running and roll a new one out deliberately.

Minimal example

resource "bahriya_project" "app" {
  handle  = "my-app"
  name    = "My Application"
  regions = ["falkenstein-1"]
}
 
resource "bahriya_container" "web" {
  handle          = "web"
  name            = "Web Server"
  image           = "nginx:alpine"
  containerport   = "80"
  healthcheckpath = "/"
  mincpu          = "100"
  minmemory       = "128"
 
  autoscalingminreplicas = "1"
  activeregions          = ["falkenstein-1"]
  project                = bahriya_project.app.id
}

With autoscaling

resource "bahriya_container" "api" {
  handle          = "api"
  name            = "API Server"
  image           = "myorg/api:v2.1.0"
  containerport   = "3000"
  healthcheckpath = "/healthz"
  mincpu          = "500"
  minmemory       = "512"
 
  autoscalingminreplicas = "2"
  autoscalingmaxreplicas = "10"
 
  activeregions = ["falkenstein-1", "helsinki-1"]
  project       = bahriya_project.app.id
}

With a private registry

If your image is in a private registry, create registry credentials and reference them:

resource "bahriya_registry" "ghcr" {
  handle   = "ghcr"
  name     = "GitHub Container Registry"
  server   = "ghcr.io"
  username = var.ghcr_username
  password = var.ghcr_token
}
 
resource "bahriya_container" "api" {
  handle          = "api"
  name            = "API Server"
  image           = "ghcr.io/myorg/api:v2.1.0"
  containerport   = "3000"
  healthcheckpath = "/healthz"
  mincpu          = "500"
  minmemory       = "512"
 
  autoscalingminreplicas = "2"
  activeregions          = ["falkenstein-1"]
  project                = bahriya_project.app.id
  registry               = bahriya_registry.ghcr.handle
}

With environment variables and secrets

resource "bahriya_secret" "db_password" {
  handle = "db-password"
  name   = "Database Password"
  value  = var.db_password
}
 
resource "bahriya_container" "api" {
  handle          = "api"
  name            = "API Server"
  image           = "myorg/api:v2.1.0"
  containerport   = "3000"
  healthcheckpath = "/healthz"
  mincpu          = "500"
  minmemory       = "512"
 
  autoscalingminreplicas = "2"
  activeregions          = ["falkenstein-1"]
  project                = bahriya_project.app.id
 
  newenvvar = [
    { key = "NODE_ENV", value = "production" },
    { key = "LOG_LEVEL", value = "info" },
  ]
 
  secretsenvvar = [
    { name = "DATABASE_PASSWORD", secret = bahriya_secret.db_password.handle },
  ]
}

With custom hostnames

resource "bahriya_container" "web" {
  handle          = "web"
  name            = "Website"
  image           = "myorg/web:latest"
  containerport   = "3000"
  healthcheckpath = "/"
  mincpu          = "250"
  minmemory       = "256"
 
  autoscalingminreplicas = "1"
  activeregions          = ["falkenstein-1"]
  project                = bahriya_project.app.id
 
  hostnames = [
    { hostname = "www.example.com" },
  ]
}

Point a CNAME record for www.example.com to the target shown in the console. TLS is provisioned automatically.

Outputs

AttributeDescription
idContainer UUID.
statusCurrent status (running, provisioning, error, etc.).
defaulthostnameThe system-generated hostname identifier.
output "api_status" {
  value = bahriya_container.api.status
}

Common mistakes

SymptomCauseFix
Container reaches error after deployHealth check fails — wrong port or pathSet containerport to the port your app listens on, and healthcheckpath to a path that returns 200.
Unknown attribute errorUsing snake_case (container_port)Use the flat name: containerport.
Project reference rejectedPassing .handle instead of .idUse bahriya_project.x.id.