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.
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
| Field | Type | Example | Description |
|---|---|---|---|
handle | string | "api" | Permanent identifier. Used in hostnames. |
name | string | "API Server" | Display name. |
image | string | "nginx:alpine" | Full image reference. A tag (or a digest) is required — an untagged image is rejected. |
containerport | string | "8080" | The port your application listens on. |
healthcheckpath | string | "/healthz" | HTTP path for health probes. Your app must respond 200 here, or the deploy will fail. |
protocol | string | "http" | Protocol your app speaks on its port: http (default), https or tcp. Used both to health check it and to deliver traffic. Optional. |
mincpu | string | "100" | Guaranteed CPU in millicores. |
minmemory | string | "128" | Guaranteed memory in megabytes. |
autoscalingminreplicas | string | "1" | Minimum number of running instances. |
activeregions | list | ["falkenstein-1"] | One or more regions to deploy to. |
project | string | bahriya_project.x.id | The 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. TheGETis 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, andhealthcheckpathis 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
| Attribute | Description |
|---|---|
id | Container UUID. |
status | Current status (running, provisioning, error, etc.). |
defaulthostname | The system-generated hostname identifier. |
output "api_status" {
value = bahriya_container.api.status
}Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
Container reaches error after deploy | Health check fails — wrong port or path | Set containerport to the port your app listens on, and healthcheckpath to a path that returns 200. |
| Unknown attribute error | Using snake_case (container_port) | Use the flat name: containerport. |
| Project reference rejected | Passing .handle instead of .id | Use bahriya_project.x.id. |