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.
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.
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:
live – process is up and serving HTTP. Use for liveness, not readiness.running – runtime started and healthy (the level bare /ready
approximates).connected – every session is currently connected to its broker; a
per-session reconnect drops below this level (503) until the session
reconnects.subscribed – every subscription has been acknowledged by the broker,
so the bridge will not miss messages. Recommended for readiness
gating and used in the example above.full – every planned receiver handler is registered, every unique desired
subscription filter is active at or above its requested QoS, no MQTT publish
remains buffered waiting for a handler, and every route is ready to dispatch.
This is the strictest gate, suitable as a pre-traffic check on initial rollout.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.
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.
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.
cmd/gobridge (the Kubernetes profile) and library embeddings honor the
config http: block: set tls_cert_file and tls_key_file (both, or
neither) to serve the admin and monitor APIs over HTTPS in-process
(cmd/gobridge/main.go). Renewed certificates hot-reload without a restart —
the server reloads the pair when either file’s modification time changes on
the next TLS handshake. The API keys may come from the environment
(GOBRIDGE_ADMIN_API_KEY, GOBRIDGE_MONITOR_API_KEY) so the ConfigMap never
carries them. See the http: field reference.gobridge-aws) does not honor the http:
block. It sources the admin/monitor listen addresses, CORS origins, and API
keys from the bootstrap config (env/SSM) rather than bridge.yaml
(deployment/aws/lib/bootstrap/app.go), and it sets
no in-process TLS — TLS terminates at the ALB in front of the task. The
http: block’s tls_cert_file / tls_key_file (and its admin/monitor
addresses and keys) are ignored on that image.