Compose Dev Services
Compose Dev Services offer a way to define custom dev services using the Compose specification.
Quarkus Dev Services automatically provisions unconfigured services in development and test mode. Most extensions providing connectivity to services also provide dev services implementation using Testcontainers behind the scenes. Each dev service has some default configuration, such as the container image to use, but is limited in terms of customization.
While this level of automation is great for most use cases, there are times when you need more control and coordination over provisioned services.
Compose Dev Services extends the Dev Services concept by allowing you to define custom dev services using the Compose specification. The Compose Specification is a developer-focused standard for defining cloud and platform agnostic container-based applications.
Compose is a widely used tool for defining and managing multi-container applications, for development and testing purposes.
A YAML description file, typically named compose.yml
, defines the services, networks, and volumes required for your application.
Docker Compose is the reference implementation and Podman Desktop also provides out-of-box support for the Compose specification.
Quarkus detects Compose files named compose-devservices.yml
(or Using specific Compose files) in your project and starts the defined services when your application runs in development or test mode.
Extensions providing dev services discover these custom services and use them instead of creating default ones.
When the dev mode or tests are stopped, the services are automatically stopped and cleaned up.
This integration provides a seamless development experience while giving you full control over your service configurations.
Prerequisites
To use Compose Dev Services, you need:
-
Compose tooling such as
docker-compose
orpodman-compose
(Note thatdocker compose
andpodman compose
commands internally call these binaries) :-
For Docker: Installing Docker Compose
-
For Podman: Setting up Compose
-
Compose Dev Services is only compatible with Compose V2. |
Using Compose Dev Services
Let’s see how to use Compose Dev Services with examples ranging from simple to more complex scenarios.
Basic Example: PostgreSQL Database
In a Quarkus project already configured to use the quarkus-jdbc-postgresql
extension,
you can create a compose-devservices.yml
file in the root of the project and define a custom service using Compose:
services:
db:
image: postgres:17
healthcheck:
test: pg_isready -U myuser -d mydb
interval: 5s
timeout: 3s
retries: 3
ports:
- '5432'
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
When you run the application in dev mode or execute tests, Compose Dev Services will automatically start the PostgreSQL service defined in the compose-devservices.yml
file,
instead of using the default dev service provided by the quarkus-jdbc-postgresql
extension.
As per the above configuration, the PostgreSQL container port 5432
will be exposed to a random host port
and the application datasource will be configured by extracting connection information such as user, password and the database name.
Multi-Service Example: Custom Network and Dependencies
For more complex scenarios, you can define custom networks and service dependencies:
services:
db:
image: postgres:17
ports:
- '5432'
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
networks:
- backend
volumes:
- postgres-data:/var/lib/postgresql/data
cache:
image: redis:7
command: redis-server --save 60 1 --loglevel warning
ports:
- '6379'
networks:
- backend
depends_on:
- db
message-broker:
image: rabbitmq:3-management
ports:
- '5672'
- '15672'
networks:
- backend
- frontend
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
api-gateway:
image: nginx:latest
ports:
- '8080:80'
networks:
- frontend
depends_on:
- message-broker
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
backend:
frontend:
volumes:
postgres-data:
Note that the compose-devservices.yml
file can be customized as needed with multiple services with dependencies to control the startup order,
custom networks to isolate service communication, and volumes for data persistence and configuration.
Service Discovery
Services started by Compose Dev Services are automatically discovered by extensions providing dev services. Each extension discovers the services it needs and configures the application accordingly.
Extensions rely on container image names and exposed ports to discover services.
For example, the PostgreSQL Dev Service looks for a service with the image name containing postgres
and the exposed container port 5432
.
When a match is found, the extension will:
-
Extract connection information from the container (connection url, credentials, database name, etc.)
-
Configure the application to use the discovered service
-
Skip creating its own default Dev Service container
Supported Extensions and Discovery Criteria
The following is the list of platform extensions with dev services support:
Extension | Image Names | Exposed Port |
---|---|---|
AMQP |
amqp, activemq-artemis, rabbitmq |
5672 |
Apicurio Registry |
apicurio |
8080 |
DB2 |
db2 |
50000 |
MySQL |
mysql |
3306 |
PostgreSQL |
postgres |
5432 |
MariaDB |
mariadb |
3306 |
Microsoft SQL Server |
mssql |
1433 |
Oracle Database |
oracle |
1521 |
Kafka |
kafka, strimzi, redpanda |
9092 |
Keycloak |
keycloak |
8080 |
Kubernetes |
kube-apiserver, k3s, kindest/node |
6443 |
MongoDB |
mongo |
27017 |
MQTT |
hivemq, eclipse-mosquitto |
1883 |
RabbitMQ |
rabbitmq |
5672 |
Pulsar |
pulsar |
6650 |
Redis |
redis |
6379 |
Infinispan |
infinispan |
11222 |
Elasticsearch |
elasticsearch, opensearch |
9200 |
Observability |
lgtm |
16686, 9411, 9090, 3000 |
Configuring custom images for service discovery
You can customize the image names used for service discovery by setting the appropriate properties in your application.properties
file.
For example, to use a custom Kafka image:
quarkus.kafka.devservices.image-name=my-custom-kafka:latest
Each extension that provides Dev Services has its own configuration properties for customizing the image name. Refer to the specific extension documentation for details.
Using specific Compose files
By default, Compose Dev Services looks for files in the project root named compose-devservices.[yml|yaml]
or docker-compose-devservices.[yml|yaml]
.
You can specify custom files to use by setting the quarkus.compose.devservices.files
property in the application.properties
file:
# Use a specific compose file for all modes
quarkus.compose.devservices.files=src/main/docker/compose.yml
# Use different compose files for different config profiles
%dev.quarkus.compose.devservices.files=src/main/docker/dev-compose.yml
%test.quarkus.compose.devservices.files=src/test/resources/test-compose.yml
This allows you to use different dev service environments depending on the Quarkus config profiles. You can also specify multiple files by separating them with commas:
quarkus.compose.devservices.files=src/main/docker/base-compose.yml,src/main/docker/extra-services.yml
Using Compose profiles
With profiles, Compose files can define a set of active profiles so started services is adjusted for various usages and environments.
You can specify the profiles to activate by setting the quarkus.compose.devservices.profiles
property in the application.properties
file:
services:
db:
image: postgres:17
profiles:
- dev
- local
ports:
- '5432'
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
%test.quarkus.compose.devservices.profiles=dev,local
Ignoring Services
You can configure Compose Dev Services to ignore specific services by adding the io.quarkus.devservices.compose.ignore
label to the service in your Compose file:
services:
db:
image: postgres:17
labels:
io.quarkus.devservices.compose.ignore: true
ports:
- '5432'
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
When a service has this label set to true
, it will be excluded from service discovery, and extensions will not use it for Dev Services.
Service Readiness
Compose Dev Services provides several ways to ensure services are ready before your application tries to use them.
Health Checks
Compose Dev Services respects the health checks defined in your Compose file. It’s recommended to configure health checks for your services to ensure they are ready before your application tries to use them:
services:
db:
image: postgres:17
healthcheck:
test: pg_isready -U myuser -d mydb
interval: 5s
timeout: 3s
retries: 3
ports:
- '5432'
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
Wait for Logs
You can configure Compose Dev Services to wait for specific log messages to appear in the container logs before considering a service ready. This is useful for services that don’t provide health checks but log a message when they’re ready.
To wait for a specific log message, add a label with the prefix io.quarkus.devservices.compose.wait_for.logs
to your service:
services:
app:
image: my-application:latest
ports:
- '8080'
labels:
io.quarkus.devservices.compose.wait_for.logs: .*Server is now running.*
You can also specify how many times a log message should appear by setting the numeric suffix value:
io.quarkus.devservices.compose.wait_for.logs.3: .Worker started.
Wait for Ports
By default, Compose Dev Services waits for all exposed ports to be available before considering a service ready. This behavior can be customized using labels:
services:
app:
image: my-application:latest
ports:
- '8080'
labels:
# Change to `true` to disable waiting for ports
io.quarkus.devservices.compose.wait_for.ports.disable: false
# Or set a custom timeout for port waiting
io.quarkus.devservices.compose.wait_for.ports.timeout: "30s"
Global Timeout
You can configure the global timeout for Dev Services startup using the quarkus.devservices.timeout
property:
quarkus.devservices.timeout=120s
This property sets the maximum time to wait for all Dev Services to start. The default is 60 seconds.
Ordering Services
Compose Dev Services starts services in the order they are defined in the Compose file.
If you need to start services in a specific order, you can use the depends_on
attribute:
services:
db:
image: postgres:17
ports:
- '5432'
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
app:
image: my-application:latest
ports:
- '8080'
depends_on:
- db
Exposing Service Configuration into the Application
Compose Dev Services automatically exposes the configuration of discovered services to your application. For example, when a database service is discovered with the following compose service description:
services:
db:
image: mysql:17
ports:
- '9906:3306'
labels:
io.quarkus.devservices.compose.jdbc.parameters: characterEncoding=UTF-8&useUnicode=true
environment:
MYSQL_USER: myuser
MYSQL_PASSWORD: mypassword
MYSQL_DATABASE: mydb
Following application properties are automatically set:
quarkus.datasource.username=myuser
quarkus.datasource.password=mypassword
quarkus.datasource.jdbc.url=jdbc:mysql://localhost:9906/mydb?characterEncoding=UTF-8&useUnicode=true
The actual values are extracted from the service labels and environment variables. The host is set to the Docker host IP address, and the port is set to the mapped port on the host.
For database services, if the label |
Custom config mapping using service labels
You can customize how environment variables and exposed ports are mapped to application configuration properties using service labels.
Mapping environment variables
To map an environment variable to a specific application configuration property,
use the io.quarkus.devservices.compose.config_map.env.<env-var-name>
label.
Let’s take the example of a MQTT broker:
services:
mqtt:
image: eclipse-mosquitto:2.0.21
ports:
- '1883'
environment:
MQTT_USER: user
MQTT_PASSWORD: pass
labels:
io.quarkus.devservices.compose.config_map.env.MQTT_USER: mp.messaging.connector.smallrye-mqtt.username
io.quarkus.devservices.compose.config_map.env.MQTT_PASSWORD: mp.messaging.connector.smallrye-mqtt.password
volumes:
- ./conf:/mosquitto/config
With the ./conf
containing the mosquitto.conf
file:
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
listener 1883
allow_anonymous false
password_file /mosquitto/config/password.txt
and the password.txt
file containing the user and encrypted password:
user:$7$101$EyGShytu3v+...==
In this example:
-
The
MQTT_USER
environment variable with the valueuser
is mapped to themp.messaging.connector.smallrye-mqtt.username
property -
The
MQTT_PASSWORD
environment variable with the valuepass
is mapped to themp.messaging.connector.smallrye-mqtt.password
property
With the |
Mapping exposed ports
To map an exposed port to a specific application configuration property,
use the io.quarkus.devservices.compose.config_map.port.<container-port>
label:
services:
my-service:
image: my-service-image
ports:
- '7432:5432'
- '9080:8080'
labels:
# Map port 5432 to quarkus.datasource.jdbc.port
io.quarkus.devservices.compose.config_map.port.5432: quarkus.datasource.jdbc.port
# Map port 8080 to quarkus.http.port
io.quarkus.devservices.compose.config_map.port.8080: quarkus.http.port
In the example above:
- Port 7432 is mapped to the quarkus.datasource.jdbc.port
property
- Port 9080 is mapped to the quarkus.http.port
property
The mapped properties will contain the host port that is mapped to the container port,
which may be different from the container port if you’re using dynamic port mapping (e.g., - '5432'
instead of - '7432:5432'
).
Exposing port mappings to running containers
In some cases, containers need to know the host ports they’re mapped to at runtime. For example, a Kafka broker needs to advertise its externally accessible address to clients.
You can use the io.quarkus.devservices.compose.exposed_ports
label to specify a file path inside the container where port mappings will be written:
services:
kafka:
image: apache/kafka-native:3.9.0
ports:
- '9092'
labels:
io.quarkus.devservices.compose.exposed_ports: /etc/kafka/docker/ports
When the container starts, Quarkus will copy a file at the specified path containing environment variable-style port mappings:
PORT_9092=54321
PORT_8080=12345
# ... other ports as needed
Each line follows the format PORT_<containerPort>=<hostPort>
, where containerPort is the port exposed by the container and hostPort is the dynamically assigned port on the host.
The container command can wait until the file is present, and then source this file to access these mappings as environment variables:
#!/bin/bash
# Wait for the ports file to be created
while [ ! -f /etc/kafka/docker/ports ]; do
sleep 0.1;
done;
# Source the file to load PORT_* variables
source /etc/kafka/docker/ports;
# Use the port mappings
export KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:$PORT_9092;
# Run the Kafka broker executable
/etc/kafka/docker/run
Controlling Service Lifecycle with Compose
Compose Dev Services provides several configuration options to control how services are started, stopped and shared between application instances.
When the application starts in development or test mode, Compose Dev Services decide whether to start services or to discover already running services based on the configuration. When the application stops, Compose Dev Services stops the services it started.
Compose Project Name
Compose uses the project name to identify resources such as services, networks, volumes, etc. in order to namespace and isolate different compose projects. This enables the discovery of existing resources and cleanup resources when the application stops.
Compose Dev Services determines the project name as follows:
-
If the
quarkus.compose.devservices.project-name
property is set, it’s used as the project name -
If the top-level
name
attribute is specified in the Compose file, it’s used as the project name -
Otherwise, the default name
quarkus-devservices-<application-name>
is chosen.
Discovering already started Compose Services
Once the project name is determined, Compose Dev Services first tries discovering existing services with that project name.
The already started compose project may have been already started by another Quarkus application running locally with the same project name,
or manually using the docker compose up
or podman compose up
commands.
Regardless of how the services were started, Compose Dev Services will configure dev services and injected configuration properties, but won’t stop the services when the application is stopped.
If no compose files are found in the project, and no |
Compose Dev Services used for tests
For Quarkus tests, a generated project name in the format quarkus-devservices-<application-name>-<random-suffix>
is used by default to ensure isolation between test runs and running dev mode services.
This way, Quarkus tests start a separate copy of the services defined in the compose files.
For example, when running continuous testing in development mode, tests will have their own isolated set of services.
You can change this behavior by setting the quarkus.compose.devservices.reuse-project-for-tests=true
property.
This allows tests to reuse services started separately in development mode.
Using Start/Stop Controls
In the default lifecycle, Compose Dev Services will start and stop services automatically when the application starts and stops.
For more fine-grained control, you can use the start-services
and stop-services
configuration properties.
When quarkus.compose.devservices.start-services=false
is set,
Compose Dev Services will only try to discover existing services with the determined project name, but won’t start any new services, even if compose files are present.
When quarkus.compose.devservices.stop-services=false
is set, services will continue running after the application stops.
This allows services to be reused by other applications or subsequent runs of the same application.
You can also configure the timeout for stopping services with the quarkus.compose.devservices.stop-timeout
property.
After the timeout, Compose Dev Services will forcefully stop the services.
The default timeout is chosen deliberately short to 1 second for fast cleanup, but you can increase it as needed:
# application.properties
quarkus.compose.devservices.stop-timeout=10s
Relation to existing Dev Services
Compose Dev Services work alongside existing Dev Services implementations provided by Quarkus extensions. The service discovery process follows this order of precedence:
-
Dev Service implementations first try locating services via shared service labels (e.g., services started by other applications)
-
If no shared services are found, they fallback to discovering services started by Compose Dev Services
-
If no Compose services are found, the default Dev Services implementation is used (typically starting a Testcontainers-based service)
When Compose Dev Service creates or discovers a project, regular dev service implementations create containers inside the compose project default network. This ensures that all services can communicate with each other using their service names as hostnames within the same network.
Configuration reference
Configuration property fixed at build time - All other configuration properties are overridable at runtime
Configuration property |
Type |
Default |
---|---|---|
Compose dev service enabled or disabled Environment variable: Show more |
boolean |
|
List of file paths relative to the project root for Compose dev service configuration, if not provided will look for compose files in the project root Environment variable: Show more |
list of string |
|
Name of the compose project, used to discover running containers, if not provided a project name will be generated Environment variable: Show more |
string |
|
Compose profiles to activate Environment variable: Show more |
list of string |
|
List of additional options to pass to compose command Environment variable: Show more |
list of string |
|
Whether to run compose up and start containers at startup, when disabled, services are discovered by project name Environment variable: Show more |
boolean |
|
Whether to run compose down and stop containers at shutdown Environment variable: Show more |
boolean |
|
Whether to use test containers Ryuk resource reaper to clean up containers Environment variable: Show more |
boolean |
|
Whether to remove volumes on compose down Environment variable: Show more |
boolean |
|
Which images to remove on compose down Locally built images, without custom tags are removed by default. Environment variable: Show more |
|
|
Env variables to pass to all Compose instances Environment variable: Show more |
Map<String,String> |
|
Scale configuration for services: Configure the number of instances for specific services Environment variable: Show more |
Map<String,Integer> |
|
Whether to tail container logs to the console Environment variable: Show more |
boolean |
|
Whether to build images before starting containers. When not provided, Compose images are built per-service Environment variable: Show more |
boolean |
|
Whether to reuse the project for tests, when disabled, a new project is created for each test run Environment variable: Show more |
boolean |
|
Timeout for stopping services, after the timeout the services are forcefully stopped, Environment variable: Show more |
|
About the Duration format
To write duration values, use the standard You can also use a simplified format, starting with a number:
In other cases, the simplified format is translated to the
|