Skip to main content

CI deploys a tested image digest

Build and test one container image in GitHub Actions, publish its immutable digest, and deploy that digest to an image-backed Bex service.

Share
Last updated on September 23, 2026
On this page

Use this workflow when CI must promote the same artifact it tested, not a second rebuild or a mutable :latest tag. The downloadable sample lives at /examples/ci-image-deploy/ (Node 22+, no npm dependencies) and in the examples catalog. Source pin: Bex 0eb2036e2.

Repository-backed services are a different path: Bex rebuilds from Git on each trigger and rejects imageUrl. See GitHub integration and How deploys work. This guide is only for an existing image-backed service.

Prerequisites

Confirm these against your installation before copying the workflow:

RequirementWhere
Image-backed service (runtime: image) already createdDocker deploys
Registry pull credential attached for private imagesDocker deploys
Machine API key (id + secret) with workspace developer roleREST & GraphQL API
Registry push access for CI (for example GHCR via GITHUB_TOKEN)GitHub package settings
Billing / payment readiness when the platform requires itBilling setup

Push permission and Bex pull credentials are separate. CI can push while the service still fails to pull if the workspace registry credential is missing.

Download the sample

bash
curl -fsSLO https://bex.co/examples/ci-image-deploy-latest.zip
curl -fsSLO https://bex.co/examples/ci-image-deploy-latest.zip.sha256
shasum -a 256 -c ci-image-deploy-latest.zip.sha256
unzip ci-image-deploy-latest.zip
cd ci-image-deploy

Configure GitHub

  1. Repository variable BEX_SERVICE_ID — the image-backed service id.
  2. Optional variables BEX_API_ORIGIN / BEX_OAUTH_TOKEN_URL for self-hosted.
  3. Secrets BEX_KEY_ID and BEX_KEY_SECRET from the dashboard API Keys page.
  4. Actions permission to write packages when using GHCR.

The workflow concurrency group is scoped to bex-image-deploy-<repository>-<BEX_SERVICE_ID> with cancel-in-progress: false. Overlapping pushes for that service queue in GitHub Actions. That does not disable dashboard deploys, deploy hooks, or other pipelines. Canceling a GitHub job does not cancel or roll back a Bex deployment that already started.

The sample builds linux/amd64 only. Do not publish a multi-platform manifest unless every listed platform is tested.

What the workflow does

  1. Build once into a local tag (ci-candidate:local).
  2. Test that image (docker run … node --test). A failing test stops the job; there is no always() deploy step.
  3. Tag and push the same local image, then resolve its registry @sha256:… digest (no second application rebuild).
  4. Run deploy.mjs with BEX_IMAGE_URL=<name>@sha256:….

deploy.mjs exchanges the machine key for a bearer token, posts { "imageUrl": "…" } to POST /v1/services/{id}/deploys, records the returned deployment id, and polls only that id until status live or a terminal failure. Mutable tag-only refs are rejected before any POST.

Offline verification

bash
BEX_CI_FIXTURE=1 \
  BEX_IMAGE_URL='ghcr.io/example/app@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' \
  node deploy.mjs
node scripts/handoff-harness.mjs

These fixtures exercise token and deploy request shapes against a loopback stack. They do not push images or create hosted deployments.

Recovery

SymptomNext step
Image tests failFix the app; deploy is not invoked
image_url_requires_sha256_digestPass name@sha256:<64-hex>, not :latest
token_exchange_failed / deploy_post_auth_failedRenew key; confirm developer role and API origin (API)
deploy_post_failed:400 mentioning repo-backedService must be image-backed; use commit deploy for Git services
Registry pull / update_failedCheck service registry credential and logs
Pre-deploy failureInspect preDeployStatus and pre-deploy logs
deploy_post_ambiguousDo not blind-retry POST; list deploys for a matching image, then decide
Deadline exceededRaise BEX_CI_DEADLINE_MS only after checking deploy status
Need prior revisionRollbacks — not automatic on CI cancel

Was this page helpful?

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex