gobridge

Container and Orchestrator Deployment

Pinning images by digest, wiring probes into an orchestrator, building your own image for non-AWS Docker or Kubernetes, and mounting configuration from a ConfigMap. Split out of Deployment Guide; these sections were nested under health checks, which is not where a reader looks for them.

Pin Images by Digest

The GoBridge project publishes no container image. The image you run is one you build: the repository root Dockerfile for the AWS profile binary (gobridge-aws), or deployment/kubernetes/Dockerfile for the reference binary (cmd/gobridge).

On AWS you normally do not build it by hand. A CDK facade with no Image set builds the image during cdk deploy and pushes it into your account’s CDK bootstrap ECR asset repository, and the task definition then references that image by digest — pinning is automatic (CDK image sources).

Everywhere else, push your build to your own registry and record the digest it prints:

docker push myregistry.example.com/gobridge:2025-09-01
docker buildx imagetools inspect myregistry.example.com/gobridge:2025-09-01 \
  --format ''

A task definition, pod spec, CDK construct or Dockerfile must reference that digest (myregistry.example.com/gobridge@sha256:...), never a tag: a moving tag makes a rebuild non-reproducible and can pull an unexpected image on the next deploy. The same rule applies to the Dockerfile base images.

The image upgrade/rollback runbook covers upgrading and rolling back between digests.

Container Orchestrator Integration

ECS Task Definition:

{
  "healthCheck": {
    "command": ["CMD", "/usr/local/bin/gobridge-aws", "-healthcheck"],
    "interval": 10,
    "timeout": 5,
    "retries": 3,
    "startPeriod": 60
  },
  "stopTimeout": 60
}

The images built from the supplied Dockerfiles ship no shell, curl, or wget, so the health check invokes the binary’s -healthcheck flag (which probes the local monitor /live endpoint). The CDK facades set stopTimeout to 60s; size it above shutdown_timeout.

Kubernetes Pod Spec:

livenessProbe:
  httpGet:
    path: /api/v1/monitor/live
    port: 8081
  initialDelaySeconds: 5
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /api/v1/monitor/ready?level=subscribed
    port: 8081
  initialDelaySeconds: 10
  periodSeconds: 5
terminationGracePeriodSeconds: 60

Admin API reachability. The readiness probe deliberately fails a paused or not-yet-connected pod, which removes it from the Service’s ready endpoints. The admin API (port 8080) then becomes unreachable through a normal ClusterIP Service exactly when an operator needs it to start or diagnose the bridge. Expose the admin port through a Service with publishNotReadyAddresses: true (or a headless Service, clusterIP: None) so a not-ready pod’s admin API stays routable; kubectl port-forward reaches it directly regardless. The liveness probe stays 200 through a clean POST /bridge/stop, so the pod is not restarted while paused.

Readiness levels. The readiness probe accepts a ?level= query parameter that controls how strict the gate is. Bare /api/v1/monitor/ready (no level) reports ready as soon as the runtime is started and healthy – before transport sessions connect or subscriptions are acknowledged – so a pod can be added to the Service endpoints while it would still miss messages. Gate production traffic on a transport-level check instead. Supported levels, least to most strict:

The probe returns 200 once the runtime has reached the requested level and 503 otherwise, so Kubernetes holds the pod out of rotation until it is genuinely ready to carry traffic. An unknown level returns 400.

Set the orchestrator’s stop/termination timeout higher than shutdown_timeout to give GoBridge enough time to drain before the orchestrator sends SIGKILL.

Kubernetes and Non-AWS Docker

The image built from the repository root Dockerfile — the same one the CDK facades build for you — runs the AWS profile: it reads its bootstrap from env/SSM and builds a DynamoDB client unconditionally. It is not a general off-AWS image — running it outside AWS without SSM and the expected bootstrap will not work.

Off AWS, run the maintained Kubernetes profile instead: a Dockerfile that builds the reference binary (cmd/gobridge — MQTT transport, memory/SQLite stores, file:// credentials, in-process HTTP API with optional TLS) using explicit GO_BUILD_TAGS=gobridge_mqtt,gobridge_native, and one manifest that runs it as a StatefulSet with a ConfigMap-mounted bridge.yaml, the admin key from a Secret, a persistent volume for the SQLite state, an init container that seeds the durable MQTT session’s baseline, and the liveness/readiness probes below. The profile is built from source and pushed to your own registry; pin it by digest like any other image. It is exercised on every integration run through probes, traffic, a ConfigMap reload, SIGTERM drain and restart (TestKubernetesProfile in tests/integration).

For additional families (SQS/DynamoDB, Azure Service Bus, AMQP, HTTP or OTel), override the Docker build argument, for example --build-arg GO_BUILD_TAGS=gobridge_mqtt,gobridge_native,gobridge_http. An override replaces the default list: retain MQTT and native stores to run the supplied manifest unchanged. Match the bridge config and any credentials, networking and store resources to the selected families. No custom composition root is needed for them; see the family table. A plain local go build of cmd/gobridge is blank, unlike this image’s default tagged build.

Kubernetes ConfigMap Config

When the bridge config comes from a ConfigMap, mount the volume (not a subPath) and point config_file_path at the file inside it. Kubernetes updates a ConfigMap volume by writing a new timestamped directory and swapping the ..data symlink atomically. The file watcher in notify mode watches the mount directory, so it sees the symlink swap; a subPath mount is a copy that Kubernetes never updates in place, so hot-reload will not fire. As a backstop the watcher re-hashes on a resync ticker (30s default) even in notify mode, which also covers network filesystems that drop inotify events.

TLS. How TLS terminates depends on which binary you run.