Appendix E: Quarkus Dev Mode
Dev services, continuous testing, live reload, and the JBang-to-Quarkus promotion workflow.
Quarkus Dev Mode is where JBang prototyping meets production-grade development. This appendix covers the features that make Camel Quarkus development fast: automatic infrastructure provisioning, live code reload, continuous testing, and the Dev UI.
The code is in examples/23-quarkus-dev/. Unlike every other example in this
tutorial, it is meant to be edited while it runs β and it needs no
infrastructure started first:
cd examples/23-quarkus-dev/quarkus
mvn quarkus:dev
Dev Services β automatic infrastructure
When you run mvn quarkus:dev, Quarkus Dev Services automatically starts containers for your dependencies. No Podman compose needed during development:
# application.properties β Dev Services start these automatically
# Kafka: starts a Redpanda container on a random port
%dev.kafka.bootstrap.servers=localhost:9092
# PostgreSQL: starts a PostgreSQL container
%dev.quarkus.datasource.db-kind=postgresql
%dev.quarkus.datasource.devservices.enabled=true
# Redis: starts a Redis container
%dev.quarkus.redis.devservices.enabled=true
When you run in dev mode, Quarkus:
- Detects
camel-quarkus-kafkain your POM β starts a Kafka broker. - Detects
quarkus-jdbc-postgresqlβ starts a PostgreSQL instance and runsinit-schemas.sql. - Detects
quarkus-redis-clientβ starts a Redis instance.
All containers are cleaned up when dev mode exits.
Dev Services back off the moment you configure the connection yourself. This is the single most common reason they appear not to work. If
application.propertiessetskafka.bootstrap.serversoutside a profile, Quarkus takes that as you naming a broker deliberately and never starts a container β with no warning and no log line to explain the silence.Scope the property to the profiles that need it:
# %dev is left alone, so Dev Services owns the broker there %prod.kafka.bootstrap.servers=localhost:9092 %test.kafka.bootstrap.servers=localhost:9092Every other example in this tutorial sets that property unconditionally, because they are all built to run against the Podman stack. That is also why Dev Services never fire in any of them.
Dev Services configuration
You can customize the containers that Dev Services provisions:
# Pin a specific image version for reproducibility
%dev.quarkus.datasource.devservices.image-name=postgres:16
%dev.quarkus.kafka.devservices.image-name=vectorized/redpanda:v24.1.1
# Pre-load SQL scripts on startup
%dev.quarkus.datasource.devservices.init-script-path=init-schemas.sql
# Expose a fixed port (instead of random) for external tool access
%dev.quarkus.datasource.devservices.port=5432
Shared Dev Services
Multiple services can share the same Dev Services container by setting %dev.quarkus.kafka.devservices.service-name=shared-kafka β services with the same service-name reuse one container. This lets you run mvn quarkus:dev in two terminals with both services on the same broker.
When to use Dev Services vs. the Podman stack
| Scenario | Use |
|---|---|
| Single-service development | Dev Services (automatic, zero config) |
| Multi-service integration testing | Podman stack (shared Kafka across services) |
| Full-stack demo | Podman stack + LGTM overlay |
| CI/CD | Podman stack (reproducible) |
Live reload
In dev mode, Quarkus watches for file changes and reloads automatically:
mvn quarkus:dev
# Change a Java file β Quarkus recompiles and restarts in ~1 second
# Change application.properties β Quarkus reloads configuration
# Change a resource file β Quarkus picks it up on next request
The reload is triggered on the next HTTP request or message consumption β Quarkus does not restart eagerly. This means you can save multiple files before triggering a rebuild, and only one restart happens.
Camel route reload
Camel routes defined in Java DSL require a full Quarkus restart on change, but Quarkusβs fast restart (~1s on a warm JVM) makes this nearly transparent. The CamelContext is stopped, rebuilt with the updated route definitions, and restarted. Change a .to() destination, save, and the next message triggers a restart with the updated route.
The --dev flag: JBang vs. Quarkus
| Feature | camel run --dev (JBang) |
mvn quarkus:dev (Quarkus) |
|---|---|---|
| Startup time | ~2 seconds | ~3-5 seconds |
| Route reload | Hot reload YAML/Java | Hot reload + full rebuild |
| Dev Services | None (use compose) | Automatic containers |
| Debugging | Limited | Full IDE debugging |
| Testing | Manual | Continuous testing |
| Production path | Export with camel export |
Direct build |
Configuration profiles
Quarkus uses configuration profiles to manage environment-specific settings. The %profile. prefix scopes a property to a specific profile:
# application.properties
# Dev profile β used by mvn quarkus:dev
%dev.kafka.bootstrap.servers=localhost:9092
%dev.quarkus.log.level=DEBUG
%dev.camel.context.name=order-service-dev
# Test profile β used by mvn test and continuous testing
%test.kafka.bootstrap.servers=localhost:9092
%test.quarkus.log.level=INFO
%test.camel.context.name=order-service-test
# Production β no prefix (default)
kafka.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS}
quarkus.log.level=WARN
camel.context.name=order-service
The active profile is determined automatically: mvn quarkus:dev activates %dev, mvn test activates %test, and java -jar or a native binary uses the unprefixed defaults. Custom profiles can be activated with -Dquarkus.profile=staging.
Continuous testing
Quarkus runs tests automatically as you code:
mvn quarkus:dev
# Press 'r' to toggle continuous testing
# Tests re-run automatically on every code change
Only tests affected by the changed code are re-run β Quarkus tracks which classes each test touches and runs the minimal set. This makes continuous testing practical even with a large test suite.
With Camel test utilities:
@QuarkusTest
public class OrderRouteTest {
@Inject
ProducerTemplate producer;
@Inject
CamelContext camelContext;
@Test
void testOrderRouting() {
// Camel route is already started by Quarkus
MockEndpoint mock = camelContext.getEndpoint("mock:result", MockEndpoint.class);
mock.expectedMessageCount(1);
producer.sendBody("direct:create-order",
Map.of("order_id", 42, "amount", 149.99));
mock.assertIsSatisfied();
}
}
Replacing endpoints for testing
Use AdviceWith to swap real endpoints (Kafka, HTTP) for mocks during tests without changing route code:
@QuarkusTest
public class OrderEnrichmentRouteTest {
@Inject CamelContext camelContext;
@Inject ProducerTemplate producer;
@Test
void testEnrichmentWithMockedKafka() throws Exception {
AdviceWith.adviceWith(camelContext, "order-enrichment", route -> {
route.replaceFromWith("direct:test-input");
route.interceptSendToEndpoint("kafka:*")
.skipSendToOriginalEndpoint()
.to("mock:kafka-output");
});
MockEndpoint mock = camelContext.getEndpoint(
"mock:kafka-output", MockEndpoint.class);
mock.expectedMessageCount(1);
producer.sendBody("direct:test-input",
"{\"order_id\": 42, \"amount\": 149.99}");
mock.assertIsSatisfied();
}
}
Dev UI dashboard
The Quarkus Dev UI is available at http://localhost:8080/q/dev-ui during dev mode. It provides a browser-based dashboard for inspecting and managing your running application.
Camel extensions in Dev UI
When camel-quarkus-core is on the classpath, the Dev UI includes Camel-specific panels:
- Routes β Lists all active routes with their status (Started/Stopped), message counts, and processing times. You can stop and restart individual routes without restarting the application.
- Endpoints β Shows every endpoint registered in the CamelContext β useful for verifying that Kafka topics, direct endpoints, and timers are wired correctly.
- Type Converters β Lists all registered type converters, helpful when debugging marshalling issues.
- Components β Shows which Camel components are loaded and their configuration properties.
Other useful panels include Configuration (all properties and effective values), Continuous Testing (results and affected-test tracking), Dev Services (running containers and connection strings), and Health (liveness/readiness probes reflecting Camel route health).
Debugging Camel routes in dev mode
IDE debugging
Quarkus dev mode supports remote debugging on port 5005 out of the box. Run mvn quarkus:dev -Ddebug (or -Ddebug=5006 for a custom port), then attach your IDE. Set breakpoints in route builders, processors, or bean methods to inspect the exchangeβs headers, body, and properties mid-flight.
Camel route tracing
Enable Camelβs exchange tracer to log every step a message takes through a route:
%dev.camel.main.tracing=true
%dev.camel.main.tracing-pattern=order-*
This logs each processor the exchange passes through, including headers and body. Restrict with tracing-pattern to avoid noise from timer and health check routes.
Common pitfalls
Port conflicts with Dev Services β If the Podman stack is already running, Dev Services will try to start duplicate containers. Disable Dev Services for the conflicting service:
%dev.quarkus.kafka.devservices.enabled=false
%dev.kafka.bootstrap.servers=localhost:9092
Stale CamelContext after refactoring β Renaming a RouteBuilder class sometimes leaves the old route registered alongside the new one. Press s in the dev mode terminal to force a full restart, or stop and restart dev mode entirely.
Orphaned containers β If dev mode is killed with kill -9 instead of Ctrl+C, Dev Services containers are not cleaned up. Remove them with podman rm -f $(podman ps -q --filter label=quarkus-dev-service).
Native build differences β Code that works in dev mode may fail in a native build due to GraalVM restrictions. Custom processors that use reflection need @RegisterForReflection; Camel Quarkus extensions handle most reflection registration automatically. Test periodically with mvn package -Dnative.
The promotion workflow
# Step 1: Prototype
camel run order-router.yaml --dev
# Step 2: Promote
camel export --runtime=quarkus --directory=order-service order-router.yaml
# Step 3: Develop
cd order-service
mvn quarkus:dev
# Step 4: Package for production
mvn package -Dnative -Dquarkus.container-image.build=true
The native build produces a GraalVM native image: ~20ms startup, ~50MB memory. Ideal for Kubernetes deployments where fast scaling matters.
Verification status: verified β examples/23-quarkus-dev/ was run on 2026-09-15. With no infrastructure started, mvn quarkus:dev brought up Dev Services for Kafka on a random port (42495) and the routes classified orders against it; mvn test passes its 2 continuous tests.