Container images
The Future AGI images: what each runs, tags, sizes, labels, health checks, users, base images and build arguments, and how to verify one.
In this page
Every Future AGI image is published to Docker Hub under futureagi/, built from the future-agi repository by its release workflow, and follows the same conventions: one tag scheme, the same OCI labels, a digest-pinned base, a HEALTHCHECK that probes the service’s real health endpoint, and a documented user. This page lists every image, what it runs and how big it may be, and the conventions they share, so you can pin, verify, probe and rebuild them.
Standalone pulls one Future AGI image, futureagi/standalone, next to Postgres and ClickHouse; Distributed and Helm run one image per service. The backend comes in two variants: the default, feature-complete one for Distributed and Helm, and a slim one inside Standalone. Pin a release tag (vX.Y.Z) or its digest for anything others rely on. Every image carries OCI labels with its release and the commit it was built from; the images are not signed yet.
Images at a glance
Setups: Standalone is the default install (./bin/install,
docker-compose.yml). Distributed runs one container per service
(./bin/install --distributed, docker-compose.distributed.yml). Helm is
Distributed on Kubernetes and uses the same images.
Size budget: the most a docker pull may download (compressed MB, 10^6
bytes, per architecture), from
scripts/image_size_budget.json. A
release checks it on every architecture before it moves a tag.
| Image | What it runs | Setups | Ports | User | Health check | Architectures | Size budget (MB) |
|---|---|---|---|---|---|---|---|
futureagi/standalone | The Standalone app container: API with an embedded Temporal worker, Temporal dev server, fi-collector, agentcc-gateway, code-evals sandbox, UI, Redis and object storage under supervisord. Built on the -slim backend | Standalone | 3000 UI, 8000 API, 4317/4318 OTLP, 8080 gateway, 9005 object storage | root | the five URLs of the compose app healthcheck | amd64, arm64 | 549 (measured 479) |
futureagi/future-agi | Backend, the default variant (feature-complete): API (SERVICE_TYPE=backend), Temporal and Celery workers, bootstrap jobs. Base of the simulation runner and of the EE and cloud images | Distributed, Helm | 80 HTTP, 50051 gRPC, 5555 Flower | root (runs as 1000:1000 on request) | GET /health/ on :80 for the API; roles with no listener are healthy while they run | amd64, arm64 | 1015 |
futureagi/future-agi:*-slim | The same backend, lean: the base of futureagi/standalone | Standalone (inside futureagi/standalone) | as above | as above | as above | amd64, arm64 | 376 (measured 341) |
futureagi/frontend | The web UI: the React app served by nginx | Distributed, Helm (Standalone serves the same files from futureagi/standalone) | 80 | root master, nginx (101) workers | GET / on :80 | amd64, arm64 | 42 (measured 37.6) |
futureagi/fi-collector | OTLP receiver that writes spans to ClickHouse; also fi-property-catalog-consumer and fi-observed-catalog-backfill | Distributed, Helm (fi-collector and fi-observed-catalog-backfill bundled in standalone) | 4317 gRPC, 4318 HTTP, 9464 admin | nonroot (65532) | GET /healthz on :9464, for the fi-collector command only | amd64, arm64 | 25 (measured 17.7) |
futureagi/agentcc-gateway | The LLM gateway | Distributed, Helm (binary bundled in standalone) | 8080 | 65532 | GET /healthz on :8080 | amd64, arm64 | 9 (measured 6.5) |
futureagi/serving | Embedding and audio/image model server | Standalone (COMPOSE_PROFILES=ml), Distributed, Helm | 8080 | root (Helm runs it as appuser, 1000) | GET /health on :8080 | amd64, arm64 | 520 amd64, 480 arm64 |
futureagi/serving:*-gpu | The same with CUDA 12.4 torch | any, on an NVIDIA host | 8080 | root (Helm: 1000) | GET /health on :8080 | amd64 | 3800 |
futureagi/code-executor | nsjail sandbox for untrusted code evals | Standalone (COMPOSE_PROFILES=sandbox), Distributed, Helm; needs privileged | 8060 | root | GET /health on :8060 | amd64, arm64 | 225 |
futureagi/future-agi-simulation-runner | Temporal worker for the simulation_runner queue: the default backend variant plus the Agent Learning Kit SDK | Distributed, Helm | none | root (from the backend) | inherited from the backend: healthy while it runs | amd64 | 1525, reported only |
futureagi/code-executor-base | Build input of code-executor (nsjail, Node.js, sandbox libraries); never run on its own | none | amd64, arm64 | counted in code-executor |
A fresh Standalone install downloads standalone, postgres:16 and clickhouse/clickhouse-server:25.3-alpine: 884 MB at most (measured 803).
Backend variants
futureagi/Dockerfile.oss builds the backend in two variants, chosen by one build argument, IMAGE_VARIANT:
Default (IMAGE_VARIANT=standard) | Slim (IMAGE_VARIANT=slim) | |
|---|---|---|
| Published as | futureagi/future-agi:vX.Y.Z (and vX.Y, latest) | futureagi/future-agi:vX.Y.Z-slim (and vX.Y-slim, latest-slim) |
| Used by | Distributed, Helm, the simulation runner, and the EE and cloud images; ./bin/install --distributed --from-source and ./bin/e2e build it | The base of futureagi/standalone, so Standalone; ./bin/install --from-source and ./bin/dev build it for Standalone |
| Contents | What earlier releases shipped: the sandbox (Daytona, E2B), billing, ops (Flower), gcp (Vertex AI SDK), langchain, rabbitmq and localizer (Claude Agent SDK) dependency groups, uv, git, Debian’s ffmpeg, every NLTK package and untrimmed site-packages. Only development tools (type stubs, the debug toolbar) moved to the dev group | Base dependencies only, no uv or git, a minimal LGPL ffmpeg build, the English NLTK data the app loads, and trimmed site-packages |
| Size | About the size of earlier releases; the budget is in Images at a glance | See Images at a glance |
Both run the same code and dependency versions (from uv.lock). What the slim variant leaves out shows up in a Standalone install as:
- Hosted agent runs on Daytona or E2B answer
501 sandbox_sdk_missing, and runs from a GitHub source501 git_unavailable. - The Vertex AI partner models (Model Garden, Gemma, Claude, Llama and Mistral on Vertex) are hidden from the model picker.
SERVICE_TYPE=flowerand the RabbitMQ channel layer are unavailable.- Error localization runs its original backend (
ERROR_LOCALIZER_BACKEND=legacy, which cannot localize simulation call audio) instead of the Claude Agent SDK one. - An audio upload in an exotic codec (AV1, WavPack, ProRes) fails with “Decoder not found”.
IMAGE_VARIANT only sets the defaults of the per-feature build arguments (Build arguments); one passed explicitly wins. A Standalone install that needs any of the above can build its app image from the default variant, from a checkout of the same tag (the image’s bootstrap runs that backend’s manage.py bootstrap_install):
docker build -t futureagi/standalone:local \
--build-arg BACKEND_IMAGE=futureagi/future-agi:vX.Y.Z deploy/standalone
Or build a slim backend with just the pieces it needs, then the app image on top of it:
docker build -f futureagi/Dockerfile.oss --build-arg IMAGE_VARIANT=slim \
--build-arg EXTRAS=sandbox --build-arg WITH_GIT=true -t futureagi/future-agi:local futureagi
docker build -t futureagi/standalone:local \
--build-arg BACKEND_IMAGE=futureagi/future-agi:local deploy/standalone
Then set FUTURE_AGI_VERSION=local in .env and run docker compose up -d.
Tags
| Tag | Points at | Use it for |
|---|---|---|
vX.Y.Z | One release | Production; pin it, or its digest |
vX.Y | The newest patch release of that minor version | Automatic patch upgrades |
latest | The newest release | Trying things out; the installers’ default |
vX.Y.Z-slim, vX.Y-slim, latest-slim | The slim variant of futureagi/future-agi, same scheme | Building your own Standalone app image; -slim exists for future-agi only |
vX.Y.Z-gpu, vX.Y-gpu, latest-gpu | The CUDA build of futureagi/serving, same scheme | NVIDIA hosts; -gpu exists for serving only |
- A release builds every architecture, checks it, and only then points all three tags of an image at one multi-architecture manifest (
build-image-multiarch.yml), so a tag never names a half-published image. - A version with a suffix (
v1.43.0-rc1, ad-hoc builds from the per-imagerelease-*.ymlworkflows) gets that one tag;vX.Yandlatestdo not move. futureagi/code-executor-basetakes exactly one immutable tag per version (base-image-publish.ymlrefuses to overwrite one);code-executorpins it.- Tags that never reach Docker Hub:
:local(./bin/install --from-source,./bin/dev),:e2e-local(./bin/e2e),:ci(CI’s local registry).
The installers choose the tag with FUTURE_AGI_VERSION in .env (empty means latest); Distributed also reads FRONTEND_VERSION, AGENTCC_GATEWAY_VERSION, FI_COLLECTOR_VERSION, SERVING_VERSION and CODE_EXECUTOR_VERSION. See Images in the configuration reference.
Labels
Every image carries the OCI annotation labels below. version, revision and created come from the VERSION, REVISION and CREATED build arguments, which the release workflows pass and each Dockerfile declares as its last instructions, so a new value changes only the image configuration and never invalidates a cached layer. A local build without them says dev and unknown.
| Label | Value |
|---|---|
org.opencontainers.image.title | e.g. Future AGI backend |
org.opencontainers.image.description | what the image runs |
org.opencontainers.image.source | https://github.com/future-agi/future-agi |
org.opencontainers.image.url | https://futureagi.com |
org.opencontainers.image.documentation | this page |
org.opencontainers.image.vendor | Future AGI |
org.opencontainers.image.licenses | Apache-2.0; Apache-2.0 AND LicenseRef-FutureAGI-Enterprise-1.0 for future-agi, standalone and the simulation runner, which ship futureagi/ee/ (see LICENSE-EE) |
org.opencontainers.image.version | the release, e.g. v1.43.0 (the -slim and -gpu variants have the same version) |
org.opencontainers.image.revision | the 40-character commit SHA the image was built from |
org.opencontainers.image.created | build time, UTC, RFC 3339 |
Extra labels: ai.futureagi.<component>.image on standalone (the backend, frontend, fi-collector, agentcc-gateway, Temporal and MinIO images it was assembled from, with digests on a release), ai.futureagi.sdk.version and ai.futureagi.backend.image on the simulation runner, and ai.futureagi.torch-backend (cpu or cu124) on serving.
Read them without pulling the image:
docker buildx imagetools inspect futureagi/standalone:latest --format '{{json .Image}}' \
| jq '."linux/amd64".config.Labels'
Or from a pulled image:
docker image inspect futureagi/standalone:latest --format '{{json .Config.Labels}}' | jq
Verifying an image
Pin the digest
docker buildx imagetools inspect futureagi/standalone:v1.43.0 prints the manifest digest. To make Compose pull exactly those bytes, name it in a docker-compose.override.yml next to docker-compose.yml:
services:
app:
image: futureagi/standalone:v1.43.0@sha256:<digest>Every release run lists, in its summary, the digest of each multi-architecture image it published. On Distributed, list the override in COMPOSE_FILE in .env: see Move to managed data stores.
Trace it to the source
The org.opencontainers.image.revision label is the commit: https://github.com/future-agi/future-agi/commit/<revision>. Before it moves any tag, the release checks that each multi-architecture image carries all the labels and the version and commit it was built from.
Signatures: not yet for the images
The images are not signed today (no cosign or Docker Content Trust signatures are published), so the first two steps are the check. Signing is planned in the release workflow (keyless cosign from GitHub Actions); once it ships, this section will give the cosign verify command and the expected certificate identity. On Kubernetes, the signed Helm chart already pins each image by digest: see Verify the chart.
Health checks
Each long-running image declares a Docker HEALTHCHECK, which docker ps shows and docker compose up --wait waits for. Kubernetes ignores it: point the pod probes at the same endpoints.
| Image | Probe | Interval / timeout / start period / retries |
|---|---|---|
standalone | GET :8000/health/, :9464/healthz, :8080/healthz, :3000/, :9005/minio/health/live (the compose app healthcheck) | 15 s / 10 s / 3600 s / 5 |
future-agi | docker/healthcheck.py: GET :80/health/ when SERVICE_TYPE=backend serves HTTP, else TCP to gRPC :50051; flower TCP :5555; workers, beat, bootstrap jobs and containers started with their own command report healthy | 30 s / 10 s / 600 s / 3 |
frontend | GET :80/ (busybox wget) | 30 s / 5 s / 10 s / 3 |
fi-collector | GET :9464/healthz, only when PID 1 is fi-collector (the consumer and backfill commands of the image serve no admin port) | 30 s / 10 s / 30 s / 3 |
agentcc-gateway | GET :8080/healthz | 30 s / 10 s / 15 s / 3 |
serving | GET :8080/health; models load on first use, not at start | 30 s / 10 s / 120 s / 5 |
code-executor | GET :8060/health | 30 s / 10 s / 20 s / 3 |
| simulation runner | Inherited from future-agi |
- The backend’s
/health/answers 503 while an embedded Temporal worker is down (tfc/asgi.py), so the probe covers the worker too. The probe sends the first non-wildcardALLOWED_HOSTSentry as itsHostheader, so tighteningALLOWED_HOSTSdoes not fail it. fi-collectorandagentcc-gatewayhave no shell, so they ship a 1.8 MB static probe,/usr/local/bin/healthcheck(its source is in both Dockerfiles).HEALTHCHECK_URL(a URL, or for the backend a comma-separated list) moves the probe offuture-agi,fi-collectorandagentcc-gateway, for example when the gateway listens on another port. Set it in that service’senvironment:, not in.env, which several services read.- No probe goes through
HTTP_PROXYorHTTPS_PROXY.
Kubernetes probe targets:
| Image | Probe |
|---|---|
future-agi | GET /health/ port 80 |
frontend | GET / port 80 |
fi-collector | GET /healthz port 9464 |
agentcc-gateway | GET /healthz port 8080 |
serving | GET /health port 8080 |
code-executor | GET /health port 8060 |
Users
| Image | User | Why |
|---|---|---|
fi-collector | nonroot (65532) | |
agentcc-gateway | 65532 | |
serving | root | An exception: existing deployments mount a root-owned model-cache volume (HF_HOME) without an fsGroup, which uid 1000 could not write. The image is ready for appuser (1000), and the Helm chart runs it that way with fsGroup: 1000 and the caches on its /models volume. NUMBA_CACHE_DIR=/tmp/numba-cache, so an arbitrary Kubernetes runAsUser also starts. Model downloads go to $HOME/.cache unless HF_HOME, SENTENCE_TRANSFORMERS_HOME and TORCH_HOME say otherwise: mount a volume at /root/.cache (or /home/appuser/.cache with user: "1000:1000") to keep them |
standalone | root | supervisord starts Redis, MinIO, nginx and the API, runs code evals as the unprivileged sandbox user (uid 18060), and keeps secrets in a root-only directory |
code-executor | root | nsjail needs root and privileged: true to create each jail’s namespaces and cgroups; evaluated code runs inside the jail as uid 0 with every capability dropped, a read-only root and no seccomp filter |
frontend | root master | The nginx master binds :80 and writes config.js at start; the workers that serve requests run as nginx (101), as in the upstream nginx image |
future-agi (both variants), simulation runner | root | The deployments that run this image today bind :80 and mount root-owned volumes (/app/backend/logs). The image is ready to run as 1000:1000: see below |
To run the backend as a non-root user: /app/backend, its logs, media and static directories, and the directories the app writes under /app/backend/tfc (logs, which the settings create on import, metadata and saml_logs for SAML, compare for dataset comparisons) belong to appuser (1000), so:
- Compose: add
user: "1000:1000"to the backend services (docker-compose.distributed.yml). Docker 20.10 and later let any user bind :80 inside a container. - Kubernetes:
runAsUser: 1000,runAsGroup: 1000,fsGroup: 1000(volumes such as a logs volume become writable), and the safe sysctlnet.ipv4.ip_unprivileged_port_start=0for port 80.
Files you mount into a non-root image (the gateway’s config and Google credentials, for example) must be readable by its user: mode 0644, or a Kubernetes Secret or ConfigMap with its default mode. File sinks you configure (a disk cache or file audit log in the gateway) need a mounted directory that user can write.
Stopping
Every image stops on SIGTERM, except frontend (SIGQUIT: nginx finishes the requests in flight). Give the containers that drain work time to do it: standalone 60 s (the API drains its Temporal worker for up to 50 s), the simulation runner more than TEMPORAL_GRACEFUL_SHUTDOWN_TIMEOUT (compose sets 330 s), Temporal workers TEMPORAL_GRACEFUL_SHUTDOWN_TIMEOUT plus a margin.
Base images
Every base is set by an ARG near the top of its Dockerfile, pinned by digest, so a rebuild of the same commit reuses the same layers and an upgrade downloads only what changed.
| Base | Pinned as | Used by |
|---|---|---|
python:3.11-slim-bookworm | digest | future-agi (so standalone and the simulation runner), serving, code-executor-base: one download for all of them |
ghcr.io/astral-sh/uv:0.11.16 | digest | Build stages of future-agi and serving; the default future-agi variant ships its uv and uvx in /usr/local/bin |
node:22.18.0 | digest | frontend build stage, never shipped |
nginx:1.31.6-alpine-slim | digest | frontend |
gcr.io/distroless/static-debian12:nonroot | digest | fi-collector |
scratch | (empty) | agentcc-gateway |
temporalio/temporal:1.9.1, ghcr.io/coollabsio/minio | digest | Binaries copied into standalone |
Deliberately not pinned by digest, each explained in its Dockerfile:
golang:1.24-alpineandgolang:1.26-alpinefloat on the minor version: build stages only, and Go patch releases carry standard-library security fixes that are compiled into the binaries.standalone’s component images and the simulation runner’s backend default to:latestfor a quick local build; a release passes this release’s images pinned by digest (the-slimbackend tostandalone, the default one to the simulation runner).code-executorpinsfutureagi/code-executor-baseby an immutable version tag; the digest is added once that version is published.
A weekly check flags a pinned tag that has moved, for example after a Debian security update, and the new digest lands in a pull request like any other change.
Build arguments
Standard, on every image:
| Argument | Default | Set by a release to |
|---|---|---|
VERSION | dev | The release tag, vX.Y.Z |
REVISION | unknown | The commit SHA |
CREATED | empty | The build time, UTC |
Per image:
| Image | Argument | Default | Effect |
|---|---|---|---|
future-agi | IMAGE_VARIANT | standard | standard or slim (Backend variants); sets the default of each argument below that is left empty |
EXTRAS | standard: sandbox,billing,ops,gcp,langchain,rabbitmq,localizer; slim: none | Optional dependency groups, comma-separated: audio, ml, voice, pii, prompt-opt, vectordb, rabbitmq, gcp, sandbox, billing, ops, langchain, localizer (pinned by uv.lock). A value replaces the variant’s list (include its groups to keep them); none installs no group | |
FFMPEG_FLAVOR | standard: debian; slim: minimal | minimal (LGPL build, about 9 MB), debian (Debian’s ffmpeg, about +145 MB), none (audio upload and video thumbnails fail) | |
WITH_GIT | standard: true; slim: false | git (+29 MB) for hosted-harness GitHub sources, which otherwise answer 501 | |
WITH_UV | standard: true; slim: false | Ships uv and uvx for images that install packages on top | |
SLIM_SITE_PACKAGES | standard: 0; slim: 1 | 1 trims site-packages (test suites, type stubs, the Google API discovery documents the app does not call) | |
STRIP_SO | standard: 0; slim: 1 | With SLIM_SITE_PACKAGES=1, 1 also strips debug sections of extension modules | |
NLTK_DATA_PROFILE | standard: full; slim: minimal | full bakes every NLTK package (all languages, the legacy packages and the archives); minimal the English data the app loads | |
PYTHON_IMAGE, UV_IMAGE | pinned | Base images | |
serving | TORCH_BACKEND | cpu | cu124 (or another CUDA index) builds the -gpu image |
frontend | VITE_HOST_API | http://localhost:8000 | API URL baked into the bundle (a container overrides it at start with VITE_HOST_API) |
VITE_ENVIRONMENT | production | ||
PRUNE_PUBLIC_ASSETS | README and marketing images | Set it empty to keep every public/ file | |
standalone | BACKEND_IMAGE, FRONTEND_IMAGE, FI_COLLECTOR_IMAGE, AGENTCC_GATEWAY_IMAGE | futureagi/<image>:latest | The component images it is assembled from |
TEMPORAL_IMAGE, MINIO_IMAGE | pinned | ||
| simulation runner | FI_VERSION | required | Agent Learning Kit SDK version (PyPI) |
BACKEND_IMAGE | futureagi/future-agi:latest | The backend it extends; the build fails on one without the sandbox SDKs and git, such as a -slim tag | |
LIVEKIT_AGENTS_VERSION | 1.8.3 | ||
code-executor | CODE_EXECUTOR_BASE | futureagi/code-executor-base:v1.1.0 | Its base; bump it whenever Dockerfile.base changes (backend-ci.yml enforces this) |
Build the images yourself
From a checkout of the future-agi repository, the installers build every image and install from them:
./bin/install --from-source # slim backend, frontend, fi-collector, gateway, then standalone, as :local
./bin/install --distributed --from-source # default backend, frontend, fi-collector, gateway, as :local
./bin/dev # the same images, with hot reload
Or build one image at a time:
docker build -f futureagi/Dockerfile.oss -t futureagi/future-agi:local \
--build-arg REVISION="$(git rev-parse HEAD)" futureagi # add --build-arg IMAGE_VARIANT=slim for the slim variant
docker build -t futureagi/frontend:local frontend
docker build -t futureagi/fi-collector:local fi-collector
docker build -t futureagi/agentcc-gateway:local agentcc-gateway
docker build -f futureagi/model_serving/Dockerfile.oss -t futureagi/serving:local futureagi/model_serving
docker build -f futureagi/code-executor/Dockerfile.base -t futureagi/code-executor-base:v1.1.0 futureagi/code-executor
docker build -t futureagi/code-executor:local futureagi/code-executor
./bin/dev and the development workflow are covered in Local development.
Dive deeper
Questions & Discussion