
Deploy Azure Communication Services (ACS) with Terraform
May 20, 2025SQL Server 2025 Integration Services Public Preview
May 20, 2025Overview
This blog post shows you how to enable SmallRye Health in Quarkus applications with health probes in Azure Container Apps. The techniques shown in this blog post show you how to effectively monitor the health and status of your Quarkus instances. The application is a “to do list” with a JavaScript front end and a REST endpoint. Azure Database for PostgreSQL Flexible Server provides the persistence layer for the app. The app uses SmallRye Health to expose application health to Azure Container Apps.
The Quarkus SmallRye Health extension provides the following REST endpoints:
- /q/health/live: Indicates that the application is up and running.
- /q/health/ready: Indicates that the application is ready to accept requests.
- /q/health/started: Indicates that the application starts successfully.
- /q/health: Aggregates all health check results for the application.
Azure Container Apps supports three types of health probes to regularly monitor the status of containerized applications:
- Liveness Probes: Identify when to restart an application.
- Readiness Probes: Check if an app instance is ready to accept traffic.
- Startup Probes: Confirm when an application completes startup.
This blog post outlines how to configure health probes on Azure Container Apps to monitor and manage Quarkus application instances.
Prerequisites
- An Azure subscription. If you don’t have an Azure subscription, create a free account before you begin.
- Prepare a local machine with Unix-like operating system installed – for example, Ubuntu, macOS, or Windows Subsystem for Linux.
- Install a Java SE implementation version 17 – for example, Microsoft build of OpenJDK.
- Install Git.
- Install Maven, version 3.9.8 or higher.
- Install Docker or Podman for your OS.
- Install the Azure CLI to run Azure CLI commands.
- Sign in to the Azure CLI by using the az login command. To finish the authentication process, follow the steps displayed in your terminal. For other sign-in options, see Sign into Azure with Azure CLI.
- When you’re prompted, install the Azure CLI extension on first use. For more information about extensions, see Use and manage extensions with the Azure CLI.
- Run az version to find the version and dependent libraries that are installed. To upgrade to the latest version, run az upgrade. This blog post requires at least version 2.65.0 of Azure CLI.
Prepare the Quarkus app
Use the following command to clone the sample Java project for this article. The sample is on GitHub.
git clone https://github.com/Azure-Samples/quarkus-azure
cd quarkus-azure
git checkout 2025-05-16
cd aca-quarkus-healthcheck
Health check
The sample application has enabled smallrye-health extension in pom.xml.
io.quarkus
quarkus-smallrye-health
The following sections show how the health, startup, and readiness checks can easily be implemented in Quarkus.
Liveness:
The LivenessCheck class is a custom health check implementation. It’s annotated with @Liveness and @ApplicationScoped to signify its role in monitoring the application’s liveness.
The call method retrieves memory usage statistics using the MemoryMXBean and determines the application’s health based on whether the used heap memory is below 90% of the maximum available memory. If this condition is met, the health check returns a status of “healthy”; otherwise, it indicates an issue.
This helps cloud environments decide if the application instance should be restarted. You can find more usage examples MicroProfile Health Example.
cat src/main/java/io/quarkus/sample/health/LivenessCheck.javapackage io.quarkus.sample.health;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.health.Liveness;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import io.quarkus.sample.TodoResource;
@Liveness
@ApplicationScoped
public class LivenessCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
long memUsed = memBean.getHeapMemoryUsage().getUsed();
long memMax = memBean.getHeapMemoryUsage().getMax();
return HealthCheckResponse.named(
TodoResource.class.getSimpleName() + ” Liveness Check”)
.status(memUsed < memMax * 0.9).build();
}
}
StartUp:
The StartupCheck class is a custom health check, annotated with @Startup to indicate it monitors the application’s startup status. It’s also marked as @ApplicationScoped for its lifecycle scope.
The call method uses the OperatingSystemMXBean to check the system’s CPU load. It calculates the current CPU usage and evaluates whether it remains below 95%. If this condition is satisfied, the application starts successfully and is in a healthy state; otherwise, it indicates a potential issue during startup.
This check helps cloud environments verify whether the application successfully starts.
cat src/main/java/io/quarkus/sample/health/StartupCheck.javapackage io.quarkus.sample.health;
import java.lang.management.ManagementFactory;
import com.sun.management.OperatingSystemMXBean;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.health.Startup;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import io.quarkus.sample.TodoResource;
@Startup
@ApplicationScoped
public class StartupCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
OperatingSystemMXBean bean = (com.sun.management.OperatingSystemMXBean)
ManagementFactory.getOperatingSystemMXBean();
double cpuUsed = bean.getCpuLoad();
return HealthCheckResponse.named(TodoResource.class
.getSimpleName() + ” Startup Check”)
.status(cpuUsed < 0.95).build();
}
}
Readiness:
The sample application uses a PostgreSQL database. Enabling smallrye-health automatically activates readiness health checks provided by the quarkus-agroal and reactive client extensions to validate the datasource. For more information, see Datasource health check. You can also implement your readiness checks by applying the annotation @Readiness in a similar fashion as the @Liveness and @Startup annotations shown in the preceding sections.
cat src/main/java/io/quarkus/sample/health/ReadinessCheck.javapackage io.quarkus.sample.health;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
import jakarta.enterprise.context.ApplicationScoped;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import io.quarkus.sample.TodoResource;
@Readiness
@ApplicationScoped
public class ReadinessCheck implements HealthCheck {
private static final String READINESS_CHECK = TodoResource.class.getSimpleName() + ” API reachable”;
private static final String API_URL = “http://localhost:8080/api”;
private boolean isHealthy() {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_URL))
.GET()
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
return response.statusCode() == 200;
} catch (Exception e) {
return false;
}
}
@Override
public HealthCheckResponse call() {
return isHealthy() ? HealthCheckResponse.up(READINESS_CHECK) : HealthCheckResponse.down(READINESS_CHECK);
}
}
Run the Quarkus app locally
The steps in this section show you how to run the app locally.
Quarkus supports the automatic provisioning of unconfigured services in development and test mode. Quarkus refers to this capability as dev services. Let’s say you include a Quarkus feature, such as connecting to a database service. You want to test the app, but haven’t yet fully configured the connection to a real database. Quarkus automatically starts a stub version of the relevant service and connects your application to it. For more information, see Dev Services Overview in the Quarkus documentation.
Make sure your container environment, Docker or Podman, is running and use the following command to enter Quarkus dev mode:
mvn clean package quarkus:dev
You might be asked if you want to send telemetry of your usage of Quarkus dev mode. If so, answer as you like.
Quarkus dev mode enables live reload with background compilation. If you modify any aspect of your app source code and refresh your browser, you can see the changes. If there are any issues with compilation or deployment, an error page lets you know. Quarkus dev mode listens for a debugger on port 5005. If you want to wait for the debugger to attach before running, pass -Dsuspend on the command line. If you don’t want the debugger at all, you can use -Ddebug=false.
The output should look like the following example:
__ ____ __ _____ ___ __ ____ ______
–/ __ / / / / _ | / _ / //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,
Press key w on the terminal where Quarkus dev mode is running. The w key opens your default web browser to show the Todo application.
You can also access the application GUI at http://localhost:8080 directly.
You can check the application’s health status by visiting the URI http://localhost:8080/q/health. The following JSON response is an example:
{
“status”: “UP”,
“checks”: [
{
“name”: “TodoResource Liveness Check”,
“status”: “UP”
},
{
“name”: “TodoResource API reachable”,
“status”: “UP”
},
{
“name”: “Database connections health check”,
“status”: “UP”,
“data”: {
“”: “UP”
}
},
{
“name”: “TodoResource Startup Check”,
“status”: “UP”
}
]
}
Additionally, a web-based GUI for health monitoring is available at http://localhost:8080/q/health-ui/, as shown in the following image:
Press key q to exit Quarkus dev mode.
Create the Azure resources
The steps in this section show you how to create the following Azure resources to run the Quarkus sample app:
- Azure Database for PostgreSQL Flexible Server
- Azure Container Registry
- Azure Container Apps Environment
- User Assigned Managed Identity
Some of these resources must have unique names within the scope of the Azure subscription. To ensure this uniqueness, you can use the initials, sequence, date, suffix pattern. To apply this pattern, name your resources by listing your initials, some sequence number, today’s date, and some kind of resource specific suffix – for example, rg for “resource group”. The following environment variables use this pattern. Replace the placeholder values in UNIQUE_VALUE and LOCATION with your own values and run the commands in your terminal.
export UNIQUE_VALUE=
export RESOURCE_GROUP_NAME=${UNIQUE_VALUE}rg-passwordless
export LOCATION=
export REGISTRY_NAME=${UNIQUE_VALUE}regpasswordless
export DB_SERVER_NAME=${UNIQUE_VALUE}dbpasswordless
export DB_NAME=demodb
export ACA_ENV=${UNIQUE_VALUE}envpasswordless
export ACA_NAME=${UNIQUE_VALUE}acapasswordless
export UAMI_NAME=”${UNIQUE_VALUE}-uami-image-pull”
Next, create a resource group by using the following command:
az group create
–name $RESOURCE_GROUP_NAME
–location $LOCATION
Create an Azure Database for PostgreSQL flexible server instance by using the following command:
az postgres flexible-server create
–name $DB_SERVER_NAME
–resource-group $RESOURCE_GROUP_NAME
–database-name $DB_NAME
–public-access None
–sku-name Standard_B1ms
–tier Burstable
–active-directory-auth Enabled
Create the Azure Container Registry and get the login server:
az acr create
–resource-group $RESOURCE_GROUP_NAME
–location ${LOCATION}
–name $REGISTRY_NAME
–sku Basic
export LOGIN_SERVER=$(az acr show
–name $REGISTRY_NAME
–query ‘loginServer’
–output tsv)
Create the Azure Container Apps environment:
az containerapp env create
–resource-group $RESOURCE_GROUP_NAME
–location $LOCATION
–name $ACA_ENV
This blog post uses YAML template to deploy custom probes on Azure Container Apps, which requires to specify managed identity to access container registry.
Run the command to create a user assigned managed identity:
az identity create –name ${UAMI_NAME} –resource-group ${RESOURCE_GROUP_NAME}
export UAMI_CLIENT_ID=$(az identity show –name ${UAMI_NAME} –resource-group ${RESOURCE_GROUP_NAME} –query “clientId” –output tsv)
export UAMI_ID=$(az identity show –name ${UAMI_NAME} –resource-group ${RESOURCE_GROUP_NAME} –query “id” –output tsv)
Grant AcrPull permission to the managed identity:
az role assignment create
–assignee ${UAMI_CLIENT_ID}
–role “AcrPull”
–scope $(az acr show –name ${REGISTRY_NAME} –query id –output tsv)
Deploy the Quarkus app to Azure Container Apps
You have set up all the necessary Azure resources to run the Quarkus app on Azure Container Apps. In this section, you containerize the Quarkus app and deploy it to Azure Container Apps.
Use the following command to build the application itself. This command uses the Jib extension to build the container image.
export TODO_QUARKUS_IMAGE_NAME=todo-quarkus-aca
export TODO_QUARKUS_IMAGE_TAG=${LOGIN_SERVER}/${TODO_QUARKUS_IMAGE_NAME}:1.0
mvn clean package -Dquarkus.container-image.build=true -Dquarkus.container-image.image=${TODO_QUARKUS_IMAGE_TAG}
Next, sign in to the Azure Container Registry and push the Docker image to the registry:
az acr login –name $REGISTRY_NAME
docker push $TODO_QUARKUS_IMAGE_TAG
Create YAML template for health probes
In this blog post, you use YAML template to define the health probes. The following YAML configuration defines the deployment of a containerized Quarkus application to Azure Container Apps with health checks and ingress for public access.
In this example, you use default values for most of health probe properties. The properties listed in the following table are required. You can add more properties and modify the values based on your requirements.
Parameter Name | Value | Description |
---|---|---|
identity.userAssignedIdentities | ${UAMI_ID} | User-assigned managed identity for creating the Container App. |
registries.server | ${LOGIN_SERVER} | The login server address of the Azure Container Registry (ACR). |
registries.identity | ${UAMI_ID} | Resource ID of the user-assigned managed identity with ‘AcrPull’ permission. |
containers.name | ${ACA_NAME} | Name of the container running the application. |
containers.image | ${TODO_QUARKUS_IMAGE_TAG} | The tag of the container image to be deployed. |
Run this command to generate the YAML configuration using the current environment variable values and save it to quarkus-probes.yaml.
cat < quarkus-probes.yaml
identity:
type: userAssigned
userAssignedIdentities:
“${UAMI_ID}”: {}
properties:
configuration:
registries:
– server: ${LOGIN_SERVER}
identity: ${UAMI_ID}
ingress:
external: true
allowInsecure: false
targetPort: 8080
template:
revisionSuffix: quarkus-health
containers:
– name: ${ACA_NAME}
image: ${TODO_QUARKUS_IMAGE_TAG}
probes:
– type: Liveness
httpGet:
path: /q/health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
– type: Readiness
httpGet:
path: /q/health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
– type: Startup
httpGet:
path: /q/health/started
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
EOF
Then, use the following command to create a Container Apps instance to run the app after pulling the image from the Container Registry:
az containerapp create
–name ${ACA_NAME}
–environment $ACA_ENV
–resource-group $RESOURCE_GROUP_NAME
–yaml quarkus-probes.yaml
Finally, connect the Azure Database for PostgreSQL Flexible Server instance to the container app using Service Connector:
# Install the Service Connector passwordless extension
az extension add –name serviceconnector-passwordless –upgrade –allow-preview true
az containerapp connection create postgres-flexible
–resource-group $RESOURCE_GROUP_NAME
–name $ACA_NAME
–target-resource-group $RESOURCE_GROUP_NAME
–server $DB_SERVER_NAME
–database $DB_NAME
–system-identity
–container $ACA_NAME
–yes
Wait for a while until the application is deployed, started and running. Then get the application URL and open it in a browser:
QUARKUS_URL=https://$(az containerapp show
–resource-group $RESOURCE_GROUP_NAME
–name $ACA_NAME
–query properties.configuration.ingress.fqdn -o tsv)
echo $QUARKUS_URL
You can access the container app health using curl $QUARKUS_URL/q/health/. The output should be similar to what you see in Run the Quarkus app locally.
Exercise: Observe health checks
In this exercise, you observe how the health checks work by stopping and starting the PostgreSQL database, and checking the application availability during each step.
To stop PostgreSQL database, run the following command:
az postgres flexible-server stop –resource-group $RESOURCE_GROUP_NAME –name $DB_SERVER_NAME
Then, check the logs of application:
az containerapp logs show –name ${ACA_NAME} –resource-group ${RESOURCE_GROUP_NAME}
The logs are similar to the following, showing that the health check detects a problem due to the database being unavailable:
{“TimeStamp”: “2025-05-16T09:08:49.0579912+00:00”, “Log”: “F 2025-05-16 09:08:49,057 INFO [io.sma.health] (vert.x-eventloop-thread-1) SRHCK01001: Reporting health down status: {“status”:”DOWN”,”checks”:[{“name”:”TodoResource API reachable”,”status”:”DOWN”},{“name”:”Database connections health check”,”status”:”DOWN”,”data”:{“”:”Unable to execute the validation check for the default DataSource: Acquisition timeout while waiting for new connection”}}]}”}
At this point, the logs show that the Container App is attempting to activate the application, but it can’t connect to the database.
To restore the database and application availability, start the PostgreSQL database with the following command:
az postgres flexible-server start –resource-group $RESOURCE_GROUP_NAME –name $DB_SERVER_NAME
Once the database is started, the application becomes available again.
Without health checks, the application won’t detect a database failure until a request tries to access the data. Implementing health checks enables proactive monitoring and faster detection of such issues.
Clean up resources
To avoid Azure charges, you should clean up unneeded resources. When the resources are no longer needed, use the az group delete command to remove the resource group and all Azure resources within it:
az group delete
–name $RESOURCE_GROUP_NAME
–yes
–no-wait
Next steps
In this blog post, you monitor the Quarkus app health leveraging SmallRye Health and health probes of Azure Container Apps. To learn more, explore the following resources:
- Health Endpoint Monitoring pattern
- Azure Container Apps
- Deploy a Java application with Quarkus on an Azure Kubernetes Service cluster
- Observe Quarkus applications with Azure Application Insights using OpenTelemetry
- Deploy serverless Java apps with Quarkus on Azure Functions
- Secure Quarkus applications with Microsoft Entra ID using OpenID Connect
- Quarkus
- Jakarta EE on Azure