Skip to main content

Docker deploys

Build a Dockerfile from Git or run a prebuilt image, including private registry authentication and image updates.

Share
Last updated on November 5, 2026
On this page

Use Docker when your project already has a Dockerfile, needs system packages, or runs a language outside Bex's native build runtimes. Bex can build the image from Git or deploy an image you have already pushed to a registry.

Choose the source​

SourceBlueprint settingsWhat Bex does
Dockerfile in Gitruntime: docker, repo, branchBuilds your Dockerfile and deploys the resulting image.
Prebuilt imageruntime: image, image.urlDeploys the supplied image without a source build.

The workload type is independent of the source: use web, pserv, worker, or cron as appropriate. See Service types.

Build a Dockerfile​

yaml
services:
  - name: api
    type: web
    runtime: docker
    repo: https://github.com/your-org/api
    branch: main
    dockerfilePath: ./Dockerfile
    autoDeployTrigger: off

Replace the repository URL with yours. Keep files needed by COPY inside the build context and check any rootDir or Docker context settings for monorepos. Your Dockerfile's default command should start the service. For Dockerfile builds, dockerCommand can override the startup command; native runtime buildCommand/startCommand settings are a different workflow.

Where caching is enabled, unchanged Dockerfile layers are reused from the service's cache; see Build caching. If a rebuild keeps serving stale layers, clear the cache for one deploy as described in How deploys work.

Inspect missing build files​

Use the Docker build-context inspector when a COPY or local ADD cannot find a file. Paste the Dockerfile, a repository-relative file inventory, your build settings and the applicable ignore files. The tool shows resolved paths, included/excluded entries, the winning ignore rule and source-line findings. It runs entirely in your browser.

From a clean checkout of the commit you intend to build, this Python command lists paths without reading file contents. It includes hidden entries and empty directories, and records symlink targets without following them:

bash
python3 - <<'PY' > /tmp/bex-build-files.txt
import os
from pathlib import Path
 
for base, directories, files in os.walk(".", followlinks=False):
    for name in sorted(directories + files):
        path = Path(base) / name
        relative = path.as_posix()
        if "\n" in relative or "\r" in relative:
            raise SystemExit("Paths with newlines are not supported")
        if path.is_symlink():
            print(f"{relative} -> {os.readlink(path)}")
        else:
            print(relative + ("/" if path.is_dir() else ""))
PY

Paste /tmp/bex-build-files.txt into the inventory input. Confirm completeness only if it describes the actual build source: local generated or untracked files may not exist in the Git checkout used by Bex. The inspector accepts at most 5,000 entries, including inferred directories; a filtered subset is not a complete inventory. A Dockerfile-specific <Dockerfile name>.dockerignore replaces the context-root .dockerignore, including when its contents are empty.

“Available” means a local source matches included inventory entries. It does not prove the build succeeds. Symlinks, remote ADD, COPY --from, heredocs, unknown variables and unsupported syntax stay explicitly unresolved. Compare the monorepo context settings if a file exists in the repository but lies outside the selected context.

Deploy an existing image​

yaml
services:
  - name: api
    type: web
    runtime: image
    image:
      url: ghcr.io/your-org/api:release-1
    numInstances: 1

Replace the image with one you can pull and run. Prefer an immutable release tag or digest so the manifest identifies the intended artifact. Follow the Blueprint guide to authenticate, validate, and apply either example. The examples deliberately omit a health-check path; configure one when your application provides a suitable endpoint.

Image-backed services are not affected by build caching: deploying a prebuilt image runs no build, so there is nothing to reuse and nothing to clear.

If the image runs today from a docker run command, paste it into the docker run to Compose converter. It maps ports, environment, volumes, networks and the restart policy into a docker-compose.yml, lists every flag it does not translate, and hands the file to the Compose → render.yaml converter. The command stays in your browser.

Use a private registry​

Create a registry credential in the target workspace through the dashboard or /v1/registrycredentials API. Creating a credential alone does not attach it to a service. Select it in the service creation form, or reference its name in the Blueprint:

yaml
services:
  - name: private-api
    type: web
    runtime: image
    image:
      url: ghcr.io/your-org/private-api:release-1
      creds:
        fromRegistryCreds:
          name: ghcr-readonly

ghcr-readonly must match a registry credential available in that workspace. For a Dockerfile that pulls private base images, use the service-level registryCredential.fromRegistryCreds.name reference instead. Store access tokens in the registry credential, not in the image URL or Git manifest.

Prepare the container for Bex​

HTTP services must listen on 0.0.0.0:$PORT; Bex defaults to port 3000. EXPOSE documents a port in an image but does not make the process listen or configure Bex's service port. If the image uses a separate setting, make it match the service listener. Bex injects PORT at runtime and drops a PORT value set in the image, so an ENV PORT=… line never reaches the process. Use an unprivileged port: tenant containers drop every Linux capability, so binding a port below 1024 fails even as root and the container crash-loops. Stock images that listen on port 80, such as nginx and httpd, need their listener moved to $PORT.

Keep required state outside the disposable container filesystem. Use a managed datastore or a persistent disk, and handle termination signals so requests or jobs can finish safely during a deployment.

Dockerfile build contract​

Bex builds a Dockerfile with BuildKit's stock Dockerfile frontend and runs the image under a hardened container policy. The tables list where that differs from what a Dockerfile can assume on a local Docker host. Paste a Dockerfile into the Dockerfile checker to find these lines in your own file; it runs in your browser. Source pin: Bex d95d017ba.

At build time​

DockerfileWhat Bex doesWhat to do
ARG NAME without a defaultPasses no --build-arg. Service environment variables are not build arguments, so the value is empty.Give the ARG a default, or read the value at runtime.
RUN --mount=type=secretPasses no build secrets to Dockerfile builds. With required=true the build fails; otherwise the mount is empty.Fetch the dependency another way, or move the step to runtime.
RUN --mount=type=cacheStarts the mount empty on every build; only image layers are cached. See Build caching.Nothing breaks; expect the download on each build.

At run time​

DockerfileWhat Bex doesWhat to do
HEALTHCHECKDoes not run it. Bex probes the service with a TCP connection to the service port, or an HTTP GET to healthCheckPath when one is set. See Health checks.Set healthCheckPath to an endpoint the app serves.
su, gosu, su-exec or setpriv in the start command, from rootAllows the container to run as root, but by default drops every Linux capability, so switching to another user fails with "operation not permitted".Set the user with USER in the Dockerfile.
sudo or another setuid program, from a non-root userDisables privilege escalation for every service, so setuid programs cannot raise privileges.Do the privileged work at build time.
Final stage FROM --platform= other than linux/amd64Runs app nodes on linux/amd64 only, so the container will not start.Drop the flag or set it to linux/amd64.
FROM scratch or a distroless final stage, with a dockerCommand overrideRuns the override as /bin/sh -c "<command>", which fails in an image without a shell. The image's own CMD and ENTRYPOINT run unchanged.Keep the start command in the image.
Shell-form CMD or ENTRYPOINTRuns /bin/sh as PID 1, which may not pass SIGTERM on. On stop, Bex sends SIGTERM and by default kills the container 30 seconds later; web and private services without a persistent disk first get a 10-second drain. See HTTP shutdown drain.Use the exec form (CMD ["node", "server.js"]), or start the program with exec.

Update an image​

For a Blueprint-managed service, update image.url, validate, and apply the manifest through Bex. Follow the resulting deployment until it completes. Updating a source setting and starting a deploy are distinct API operations; when changing the image through service settings, use the deployment action to release the saved source.

Changing an upstream mutable tag does not by itself submit a Bex deployment. For a predictable release, publish a new versioned image and deploy that reference. To promote the exact image CI tested (immutable digest) from GitHub Actions, see CI deploys a tested image digest. Avoid patching API-managed Kubernetes resources directly: the control plane owns their desired configuration.

Troubleshoot​

Check build logs for Dockerfile/context errors. For image pull failures, verify the image name, tag or digest, registry access, and attached credential. If the image pulls but the process fails, inspect runtime logs, architecture support, its startup command, listener port, and required environment variables. Bex app nodes run linux/amd64, so an image built only for arm64 will not start; see the Dockerfile build contract. If a Dockerfile rebuild at the right commit still serves old behavior, suspect stale cached layers and work through My change did not ship.

See Web services, Secrets, and Rollbacks.

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