Skip to main content
Last updated on

All-in-One Image for Integration Testing

Ecosystem projects around Apache Doris — connectors, SDKs, data integration tools — usually need a real Doris instance in their e2e / CI runs rather than a mock. The official all-in-one image is built for exactly that: one FE and one BE in a single container, ready to use on startup, with a health check, so it can be dropped in as a test fixture.

This document describes what the image provides, how to use it, and how to build it yourself.

Note

This image is a test fixture designed for integration testing: a single FE and a single BE, one replica, memory settings tuned down for CI runners, and no data persistence by default. Do not use it for production deployment.

Image overview

Image tags look like apache/doris:all-in-one-<version>, for example apache/doris:all-in-one-4.1.3. Every example below uses 4.1.3 — replace it with the version you need. For the available tags, see the apache/doris repository on Docker Hub.

The image is assembled from the official apache/doris:fe-<version> and apache/doris:be-<version> images, so what runs inside it is the released artifact itself. Only two things are done for the CI use case: debug info is stripped from doris_be (.symtab is kept, so crash backtraces still resolve function names), and the JNI scanner directories the tag does not need are removed.

The behaviors that matter for integration testing:

FeatureDescription
Ready to useFE startup, ALTER SYSTEM ADD BACKEND registration of the BE, and waiting for the BE to come online all happen inside the container. No external script is involved
Health checkThe image ships a HEALTHCHECK that turns healthy only once FE is ready and the BE is online, usually within 20 seconds. CI can wait on that status instead of calling sleep
Single replicaforce_olap_table_replication_num = 1 is set, so CREATE TABLE does not need replication_num
Bounded resourcesMemory is tuned down for CI runners: FE heap -Xmx2048m, the BE-side JNI heap -Xmx1024m, and BE mem_limit = 40%
Fail-fastIf either FE or BE exits, the container exits non-zero right away instead of restarting silently, so a failure surfaces immediately in CI
Graceful shutdowndocker stop stops BE first, then FE, and exits 0
Multi-architectureOne tag covers both linux/amd64 and linux/arm64

The two tags: base and -full

TagCoversImage size (4.1.3, uncompressed)
apache/doris:all-in-one-<version>Internal tables, Hive, Iceberg (including system tables), Paimon, JDBC catalogs, external table writeback, Java UDFAbout 2.5 GB
apache/doris:all-in-one-<version>-fullEverything above, plus Hudi, Trino connector, MaxComputeAbout 3.0 GB

The sizes were measured on 4.1.3. For reference, the same payload without stripping and pruning is about 4.9 GB.

Only pick -full if the tests actually read Hudi tables, go through the Trino connector, or use MaxCompute. Otherwise use the tag without the suffix.

The difference between the two tags comes from how BE loads its scanners: at startup BE enumerates the directories under be/lib/java_extensions/ to load JNI scanners. There is no list and no configuration key, so a tag supports exactly the formats whose directory it ships. Note that Hive and Iceberg data reads go through BE's native parquet / orc reader rather than these JNI scanners, so both tags support them.

Multi-architecture support

apache/doris:all-in-one-4.1.3 is an OCI image index carrying one manifest for linux/amd64 and one for linux/arm64. A docker pull picks the one matching the host automatically, the same way apache/doris:fe-4.1.3 does, so CI does not need different tags for runners of different architectures.

docker buildx imagetools inspect apache/doris:all-in-one-4.1.3
Note

A Doris BE generally cannot run under cross-architecture emulation. Starting the amd64 image with --platform linux/amd64 on Apple Silicon, for instance, segfaults, and the official apache/doris:be-* images behave the same way. Run the image on real hardware of the matching architecture.

Using it in integration tests

Connection information

ItemDescription
MySQL protocolPort 9030, user root, no password
FE HTTPPort 8030, /api/health needs no authentication
BE HTTPPort 8040, the stream load endpoint
BE heartbeatPort 9050, used inside the container, normally not mapped to the host

Plain docker run

docker run -d --name doris \
-p 9030:9030 -p 8030:8030 -p 8040:8040 \
apache/doris:all-in-one-4.1.3

# Wait until the container is healthy
until [ "$(docker inspect -f '{{.State.Health.Status}}' doris)" = healthy ]; do sleep 1; done

mysql -uroot -h127.0.0.1 -P9030 -e "SHOW BACKENDS"

The image ships a MySQL client, so if the host does not have one, run it inside the container instead:

docker exec doris mysql -uroot -h127.0.0.1 -P9030 -e "SHOW BACKENDS"

Docker Compose

With depends_on and service_healthy, the test container starts only after Doris is genuinely usable:

services:
doris:
image: apache/doris:all-in-one-4.1.3
ports: ["9030:9030", "8030:8030", "8040:8040"]

integration-test:
image: my-project-tests:latest
depends_on:
doris:
condition: service_healthy

GitHub Actions

As a service container, GitHub Actions waits for the container to become healthy before running the steps, so the test code does not need its own retry-and-wait logic:

jobs:
e2e:
runs-on: ubuntu-latest
services:
doris:
image: apache/doris:all-in-one-4.1.3
ports: ['9030:9030', '8030:8030', '8040:8040']
steps:
- uses: actions/checkout@v4
- run: mvn -B verify -Pe2e

Adjusting the configuration

The image already carries a set of integration-test defaults. To change something, use the two environment variables below to append configuration items; they are appended to the end of fe.conf and be.conf respectively at startup, and the last assignment wins:

docker run -d --name doris \
-p 9030:9030 -p 8030:8030 -p 8040:8040 \
-e BE_CONFIG_EXTRA="mem_limit = 80%" \
-e FE_CONFIG_EXTRA="qe_max_connection = 2048" \
apache/doris:all-in-one-4.1.3
Environment variableEffect
FE_CONFIG_EXTRAAppended to fe.conf. Separate multiple items with newlines
BE_CONFIG_EXTRAAppended to be.conf. Separate multiple items with newlines

Data persistence: there is none by default, so the data is gone once the container is removed, which is usually what CI wants. To keep the data, mount /opt/apache-doris/fe/doris-meta and /opt/apache-doris/be/storage. The startup path is idempotent, so the same data directory can be started again and again.

Troubleshooting

  • docker logs <container> carries the entrypoint's startup log and FE's console stream, which tells you where the cluster is stuck during startup.
  • The full logs live inside the container at fe/log/fe.log and be/log/be.INFO.
  • A non-zero container exit means FE or BE died. The log right before the exit names which one it was and which log file to read.
  • Tests handling larger volumes of data may need vm.max_map_count raised on the host (sysctl -w vm.max_map_count=2000000). It is not a namespaced sysctl, so the container cannot set it, which is why the image skips start_be.sh's check for it.

Building the image yourself

The Dockerfile and the build script live under docker/runtime/all-in-one/<release-line>/ in the main Doris repository, where 4.1 is currently available. Release lines differ enough in artifact layout that each gets its own directory instead of a version switch inside a single Dockerfile.

build.sh is the only entry point. The build context is always the repository root, and the script itself can be run from any directory:

# Build both the base and the -full tag from the official 4.1.3 component images
# (host architecture only)
./build.sh -v 4.1.3

# Build base only, then run a smoke test against it
./build.sh -v 4.1.3 -f base -t

# Build from a local ./output, which is handy for verifying your own changes
# Requires a completed Doris build producing ./output/fe and ./output/be
./build.sh -v dev -s local

Without --platform, only the host architecture is built. Multi-architecture builds need it spelled out, for example ./build.sh -v 4.1.3 --platform linux/amd64,linux/arm64 --push. Since a cross-architecture build goes through emulation and takes a long time, building each architecture natively on its own machine and then joining the results with docker buildx imagetools create is the better choice for a release.

./build.sh --help lists the remaining options.

tip

For implementation details — what the image prunes, why some directories that look like external table extras must not be pruned, where the size goes, and how to run the smoke test — see the README in that directory.