Home Java Deploying Spring Boot to Azure: App Service & AKS Guide for Java Devs
Advanced 7 min · July 14, 2026

Deploying Spring Boot to Azure: App Service & AKS Guide for Java Devs

Learn to deploy Spring Boot 3.2+ to Azure App Service and AKS.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 20-25 min read
  • Java 21+ and Spring Boot 3.2+ project with Maven or Gradle
  • Active Azure subscription (free tier works for testing)
  • Azure CLI installed and logged in (az login)
  • Docker Desktop installed for container builds
  • Basic understanding of Kubernetes concepts (pods, services, deployments)
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

• Use Azure App Service for simple PaaS deployments with built-in scaling and SSL termination • Choose Azure Kubernetes Service (AKS) when you need fine-grained control over container orchestration and multi-service architectures • Always configure Azure-specific connection pooling for managed databases like Azure SQL or MySQL • Use Azure Key Vault to store secrets and Azure AD for authentication in production • Implement health checks with Spring Boot Actuator and configure Azure Application Insights for observability

✦ Definition~90s read
What is Deploying Spring Boot Applications to Azure?

Deploying Spring Boot to Azure means packaging your Java application as either a JAR or Docker container and running it on Microsoft's cloud platform through either Azure App Service (PaaS) or Azure Kubernetes Service (AKS) with managed scaling, monitoring, and CI/CD integration.

Deploying a Spring Boot app to Azure is like moving from a home kitchen to a professional restaurant.
Plain-English First

Deploying a Spring Boot app to Azure is like moving from a home kitchen to a professional restaurant. App Service is like hiring a full-service caterer that handles the kitchen, plates, and cleanup—you just bring your recipes (your code). AKS is like renting a commercial kitchen space where you control every burner, shelf, and schedule, but you also have to manage the staff and inventory yourself. Both can serve your customers (users), but the level of control and responsibility differs dramatically.

You've built a solid Spring Boot application—maybe a payment-processing service or a real-time analytics dashboard—and now it's time to put it in front of users. Azure offers two primary paths: Azure App Service, a fully managed PaaS that abstracts away infrastructure, and Azure Kubernetes Service (AKS), a managed Kubernetes cluster that gives you container orchestration at scale. Choosing between them isn't just a technical decision; it's a trade-off between operational overhead and control. In this guide, I'll walk you through both deployment strategies using Spring Boot 3.2 and Java 21. We'll cover the real-world gotchas that official Microsoft docs gloss over: connection pooling timeouts with managed databases, secret management without hardcoding, and how to handle graceful shutdowns in Kubernetes. I've been burned by each of these in production—once during a Black Friday sale when our App Service app crashed because of a misconfigured HikariCP pool against Azure SQL. You'll get code examples for Dockerizing your app, configuring Azure-specific health checks, and setting up CI/CD pipelines with GitHub Actions. By the end, you'll know exactly which Azure service fits your use case and how to avoid the six most common deployment failures I've seen in the field.

Azure App Service: The Quick Path to Production

Azure App Service is your best bet when you want to deploy a Spring Boot application without managing servers. It handles SSL termination, auto-scaling, and load balancing out of the box. For a typical REST API or SaaS billing service, this is often sufficient. The key is to package your app as a fat JAR and configure it via Azure-specific application settings. Let me show you how to deploy a Spring Boot 3.2 app with Maven. First, ensure your pom.xml includes the Azure Web App plugin. Then, configure your application.properties for Azure: set server.port to 8080 (App Service expects this), and use Azure-specific datasource URLs if connecting to Azure SQL or MySQL. The deployment command is straightforward: mvn azure-webapp:deploy. But here's the catch—Azure App Service runs your JAR with a default memory limit of 2GB. If your app uses more, you'll get OOM kills. Always set the JAVA_OPTS environment variable in Azure Portal under Application Settings: -Xmx2048m -Xms1024m. Also, never hardcode secrets; use Azure Key Vault references in your app settings like @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/db-password/).

pom.xml (Azure plugin section)XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<plugin>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-webapp-maven-plugin</artifactId>
    <version>2.13.0</version>
    <configuration>
        <schemaVersion>v2</schemaVersion>
        <subscriptionId>your-sub-id</subscriptionId>
        <resourceGroup>spring-boot-rg</resourceGroup>
        <appName>payment-service-app</appName>
        <pricingTier>P1v2</pricingTier>
        <region>eastus</region>
        <runtime>
            <os>linux</os>
            <javaVersion>Java 21</javaVersion>
            <webContainer>Java SE</webContainer>
        </runtime>
        <appSettings>
            <property>
                <name>JAVA_OPTS</name>
                <value>-Xmx2048m -Xms1024m</value>
            </property>
        </appSettings>
    </configuration>
</plugin>
Output
mvn azure-webapp:deploy # Deploys to Azure App Service
⚠ Don't Use Default JVM Settings
📊 Production Insight
In production, always enable auto-heal in App Service. Under App Service > Diagnose and Solve Problems > Auto-Heal, configure rules to recycle the app if it consumes >90% memory for 5 minutes. This saved us during a memory leak incident.
🎯 Key Takeaway
Azure App Service is ideal for single-service deployments where you want minimal ops overhead, but you must configure JVM heap and connection pools for Azure's environment.

What the Official Docs Won't Tell You

Microsoft's official documentation for Spring Boot on Azure is decent for getting started, but it conveniently omits the painful realities. First, Azure App Service does NOT support WebSocket scaling well. If your Spring Boot app uses WebSocket (e.g., for real-time notifications), you'll hit a wall because App Service's load balancer doesn't support WebSocket affinity across multiple instances. You'll see clients randomly disconnecting. The fix is to use Azure SignalR Service, but that adds cost and complexity—something the docs gloss over. Second, Azure Kubernetes Service (AKS) has a notorious issue with Azure CNI networking: pod-to-pod latency spikes when you exceed 30 pods per node. The official docs recommend using Azure CNI Overlay (in preview) or Kubenet, but they don't tell you that Kubenet has limited network policies. I learned this the hard way when our analytics service's inter-pod communication timed out during a data backfill. Third, both App Service and AKS have a subtle issue with Spring Boot's graceful shutdown. If you don't configure spring.lifecycle.timeout-per-shutdown-phase, Azure's load balancer will keep sending traffic to your shutting-down pod, causing 502 errors. The docs show you how to enable graceful shutdown but not how to coordinate with Azure's health probes. You need to set the health endpoint to return 503 during shutdown, which requires a custom GracefulShutdownHandler. Finally, never trust the default Azure SQL connection string in docs—it uses integrated security, which won't work with Spring Boot's HikariCP. You must use the JDBC URL with username/password or Azure AD managed identity.

GracefulShutdownConfig.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Configuration
public class GracefulShutdownConfig {
    @Bean
    public GracefulShutdownHandler gracefulShutdownHandler() {
        return new GracefulShutdownHandler();
    }

    @Bean
    public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> customizer() {
        return factory -> {
            factory.setRegisterShutdownHook(false);
        };
    }
}

class GracefulShutdownHandler {
    private volatile boolean shuttingDown = false;
    
    @EventListener
    public void onApplicationEvent(ContextClosedEvent event) {
        shuttingDown = true;
    }
    
    @GetMapping("/actuator/health")
    public ResponseEntity<Health> health() {
        if (shuttingDown) {
            return ResponseEntity.status(503).body(
                Health.down().withDetail("reason", "shutting down").build()
            );
        }
        return ResponseEntity.ok(Health.up().build());
    }
}
Output
During rolling update, AKS load balancer stops sending traffic to the pod before it's terminated, preventing 502 errors.
🔥AKS Networking Reality Check
📊 Production Insight
I once spent 12 hours debugging random 502s during AKS rolling updates. The root cause? Azure's load balancer probe interval (5 seconds) was slower than Spring Boot's shutdown timeout (30 seconds). Set Kubernetes readinessProbe.periodSeconds to 1 and Spring's shutdown timeout to 60s.
🎯 Key Takeaway
Official Azure docs omit critical details about WebSocket scaling, AKS networking, and graceful shutdown. Always test under load before production.

Dockerizing Your Spring Boot App for Azure

Whether you target App Service (which supports custom containers) or AKS, you need a production-grade Docker image. Don't use the default spring-boot:2.7 image—it's based on Ubuntu and bloated. Instead, use Eclipse Temurin's Alpine-based JDK 21 image, which reduces image size from 400MB to 180MB. Your Dockerfile should use multi-stage builds: first stage compiles with Maven, second stage runs the JAR. This keeps your final image lean and secure. For AKS, you must handle graceful shutdown and health checks in the container. Set the ENTRYPOINT to use exec form so signals (SIGTERM) propagate to the Java process. Never use shell form (CMD java -jar ...) because it spawns a shell that eats signals. Also, configure Spring Boot's management.endpoint.health.probes.enabled=true to expose liveness and readiness endpoints separately. This is critical for Kubernetes to restart unhealthy pods without impacting traffic. For App Service with containers, you must also set WEBSITES_ENABLE_APP_SERVICE_STORAGE=true if your app writes to the filesystem (e.g., for logs). Without this, container restarts wipe your data. One more thing: always pin your base image to a specific digest, not a tag like 'latest'. I've seen base image updates break production builds because of incompatible glibc versions.

DockerfileDOCKERFILE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN ./mvnw package -DskipTests

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENV SPRING_PROFILES_ACTIVE=azure
HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
Output
docker build -t payment-service:1.0.0 . && docker run -p 8080:8080 payment-service:1.0.0
⚠ Signal Handling in Containers
📊 Production Insight
In production, add a .dockerignore file to exclude .git, target, and .m2 directories. This reduces build context from 500MB to 50KB and speeds up CI/CD builds by 40%.
🎯 Key Takeaway
Use multi-stage builds with Alpine JDK 21, configure health checks, and use exec form ENTRYPOINT for proper signal handling in containers.

Deploying to Azure Kubernetes Service (AKS)

AKS gives you Kubernetes without the control plane headache, but you still manage node pools, scaling, and networking. For Spring Boot, you'll deploy using Kubernetes manifests. Start by creating a namespace and a deployment with resource requests and limits. Never run a Spring Boot pod without specifying CPU and memory limits—AKS will overcommit and OOMKill your pods. Use 2 CPU cores and 4GB memory as a baseline for a typical REST API. Configure horizontal pod autoscaling based on CPU utilization (target 70%) or custom metrics like HikariCP active connections. For the service, use ClusterIP for internal communication and an Azure Load Balancer for external traffic. If you need SSL, use cert-manager with Let's Encrypt or Azure Key Vault via the Secrets Store CSI driver. The biggest gotcha with AKS is persistent storage. Spring Boot apps that write to disk (e.g., file uploads, logs) need Azure Disk or Azure Files. Use StatefulSet only if you need stable network identities; otherwise, use Deployments with PersistentVolumeClaims. For logging, configure Spring Boot to output JSON logs and use Azure Monitor for Containers or Fluentd. Do NOT rely on kubectl logs in production—they're ephemeral and don't scale. Finally, set up Azure DevOps or GitHub Actions to deploy to AKS. Use Helm charts for managing environment-specific configurations. I recommend keeping your Helm values in a separate repo with Azure Key Vault integration for secrets.

deployment.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      containers:
      - name: payment-service
        image: myacr.azurecr.io/payment-service:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "1"
            memory: "2Gi"
          limits:
            cpu: "2"
            memory: "4Gi"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 5
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "aks"
Output
kubectl apply -f deployment.yaml # Deploys to AKS
🔥AKS Cost Optimization
📊 Production Insight
For high-traffic payment services, use pod anti-affinity rules to spread pods across different availability zones. This prevented a complete outage when one Azure zone went down during a regional failure.
🎯 Key Takeaway
AKS deployments require explicit resource limits, separate liveness/readiness probes, and JSON logging. Use Helm for configuration management across environments.

Configuring Azure-Specific Connection Pooling

Azure managed databases (Azure SQL, MySQL, PostgreSQL) have different characteristics than on-premises instances. The primary issue is connection latency: Azure's gateway adds 10-30ms per new connection. If your Spring Boot app uses HikariCP with default settings (10 connections, 30-second timeout), you'll see connection timeouts under load. The fix is to increase maximumPoolSize to at least 30 for production, set connectionTimeout to 60 seconds, and enable leakDetectionThreshold to catch unclosed connections. Also, configure a validation query (SELECT 1 for MySQL, SELECT 1 for SQL Server) to ensure connections are healthy. For Azure SQL specifically, use the sqlserver driver with integratedSecurity=false and encrypt=true. Another Azure-specific issue: connection pooling with Azure AD Managed Identity. Instead of a username/password, you can use DefaultAzureCredential with the JDBC driver. This is more secure but requires setting the authentication=ActiveDirectoryManagedIdentity parameter in the JDBC URL. However, this only works if your App Service or AKS pod has a managed identity assigned. I've seen teams spend days debugging 'Login failed for user' errors because they forgot to assign the identity to the App Service. Also, always set the connection pool's idleTimeout to 10 minutes and maxLifetime to 30 minutes to match Azure's gateway idle timeout. If you don't, you'll get stale connections that cause random SQL exceptions.

application-azure.propertiesPROPERTIES
1
2
3
4
5
6
7
8
9
10
11
12
# Azure SQL connection pool configuration
spring.datasource.url=jdbc:sqlserver://myserver.database.windows.net:1433;database=mydb;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;
spring.datasource.username=myuser@myserver
spring.datasource.password=${DB_PASSWORD}
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.leak-detection-threshold=10000
spring.datasource.hikari.pool-name=AzureSQLPool
spring.datasource.hikari.validation-timeout=5000
Output
Connection pool configured for Azure SQL with 30 max connections and 60-second timeout.
⚠ Don't Use Default Pool Settings
📊 Production Insight
Monitor HikariCP metrics via Micrometer (hikaricp_connections_active, hikaricp_connections_pending). In production, we set up an Azure Monitor alert when pending connections exceed 5 for more than 1 minute—this catches pool exhaustion early.
🎯 Key Takeaway
Azure managed databases require larger connection pools, longer timeouts, and validation queries. Use managed identity for passwordless authentication when possible.

CI/CD with GitHub Actions for Azure Deployments

Manual deployments are a recipe for disaster. Use GitHub Actions to automate builds and deployments to both App Service and AKS. For App Service, the workflow is simple: build the JAR, deploy using azure/webapps-deploy@v2. For AKS, you need to build a Docker image, push to Azure Container Registry (ACR), then update the Kubernetes deployment. The key is to use OIDC (OpenID Connect) for authentication instead of storing Azure secrets in GitHub. This eliminates the risk of credential leaks. Configure a federated identity credential in Azure AD for your GitHub repository. In the workflow, use azure/login@v1 with client-id, tenant-id, and subscription-id. For AKS deployments, use azure/aks-set-context@v1 to connect to your cluster. Then use kubectl to apply the manifest. But here's the critical part: always use image tag based on the Git commit SHA, not 'latest'. This ensures traceability and allows rollbacks. For App Service, set the WEBSITE_RUN_FROM_PACKAGE setting to 1—this mounts the JAR directly from Azure Storage, improving cold start times. I've seen cold starts drop from 30 seconds to 5 seconds with this. Also, add a smoke test step after deployment that hits the health endpoint. If it fails, automatically rollback to the previous version. This saved us when a misconfigured Redis connection string broke production for exactly 47 seconds until rollback completed.

.github/workflows/deploy-aks.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
name: Deploy to AKS
on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'temurin'
    - name: Build with Maven
      run: mvn package -DskipTests
    - name: Azure Login
      uses: azure/login@v1
      with:
        client-id: ${{ secrets.AZURE_CLIENT_ID }}
        tenant-id: ${{ secrets.AZURE_TENANT_ID }}
        subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Build and Push to ACR
      run: |
        az acr build --registry myacr --image payment-service:${{ github.sha }} .
    - name: Set AKS Context
      uses: azure/aks-set-context@v1
      with:
        resource-group: my-rg
        cluster-name: my-aks
    - name: Deploy to AKS
      run: |
        sed -i "s|image:.*|image: myacr.azurecr.io/payment-service:${{ github.sha }}|" deployment.yaml
        kubectl apply -f deployment.yaml
    - name: Smoke Test
      run: |
        sleep 30
        kubectl rollout status deployment/payment-service -n production
Output
GitHub Actions workflow deploys to AKS with OIDC authentication and commit-SHA-based image tags.
🔥Rollback Strategy
📊 Production Insight
In our CI/CD pipeline, we added a canary deployment step: route 10% of traffic to the new version for 5 minutes before full rollout. This caught a memory leak that only manifested under real traffic.
🎯 Key Takeaway
Use OIDC for Azure authentication, commit-SHA image tags for traceability, and automatic rollback on failed smoke tests.

Monitoring and Observability with Azure Application Insights

You can't debug production issues without observability. Azure Application Insights integrates with Spring Boot via Micrometer and OpenTelemetry. The setup is straightforward: add the applicationinsights-spring-boot-starter dependency to your pom.xml, and configure the connection string in application.properties. This automatically captures HTTP requests, dependencies (database calls, Redis), and custom metrics. For Spring Boot-specific metrics, enable Micrometer's JVM metrics (jvm.memory.used, jvm.gc.pause) and HikariCP metrics (hikaricp.connections.active). These are invaluable for diagnosing memory leaks or connection pool issues. The real power comes from distributed tracing. Use @io.opentelemetry.instrumentation.annotations.WithSpan on your service methods to trace business logic. In production, we trace every payment transaction from HTTP request to database commit. This helped us identify a slow SQL query that only appeared under high concurrency—the trace showed the database call taking 5 seconds due to a missing index. Also, set up Application Insights availability tests to ping your health endpoint from multiple Azure regions. This catches regional outages before your customers do. For AKS, use Azure Monitor for Containers to collect pod logs and metrics. Configure Spring Boot to output JSON logs with the Logstash encoder—this makes log parsing in Azure Log Analytics much easier. One critical setting: always set applicationinsights.internal.min-telemetry-processor-delay to 1000ms to avoid overwhelming the ingestion endpoint during traffic spikes.

pom.xml (Application Insights dependency)XML
1
2
3
4
5
6
7
8
9
10
<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>applicationinsights-spring-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-azure-monitor</artifactId>
    <version>1.12.5</version>
</dependency>
Output
mvn dependency:tree # Verify dependencies are resolved
⚠ Sampling Rate Matters
📊 Production Insight
During a major incident, our traces showed that a Redis cache miss was causing a 2-second delay per request. We added a cache-aside pattern with a TTL of 60 seconds, reducing p95 latency from 3 seconds to 200ms.
🎯 Key Takeaway
Use Application Insights with Micrometer for JVM and connection pool metrics. Enable distributed tracing with OpenTelemetry to debug multi-service transactions.

Scaling and Performance Tuning on Azure

Scaling Spring Boot on Azure requires understanding both application-level and infrastructure-level tuning. For App Service, you can scale vertically (increase instance size) or horizontally (add instances). Horizontal scaling is more cost-effective for Spring Boot because it's multi-threaded. Set the minimum number of instances to 2 for redundancy, and configure auto-scaling based on CPU > 70% or memory > 80%. But beware: App Service's auto-scaling has a cooldown period of 5 minutes. If your traffic spikes suddenly (e.g., during a flash sale), you'll see errors before new instances spin up. The fix is to use a pre-warmed instance pool or set a higher minimum instance count. For AKS, use the Horizontal Pod Autoscaler (HPA) with custom metrics. Don't rely solely on CPU—Spring Boot apps often become memory-bound before CPU-bound. Use the Kubernetes Event-driven Autoscaling (KEDA) to scale based on queue length (e.g., Azure Service Bus) or HTTP request rate. For performance tuning, start with JVM heap settings: set -Xms and -Xmx to the same value to avoid heap resizing. Use G1GC garbage collector for low-latency services: -XX:+UseG1GC -XX:MaxGCPauseMillis=100. For AKS, also configure the pod's requests and limits carefully. Over-provisioning wastes money; under-provisioning causes OOM kills. Use the Vertical Pod Autoscaler (VPA) in recommendation mode to find optimal values. One more thing: Azure's network latency between services can be 1-5ms even in the same region. If your Spring Boot service calls another microservice, use WebClient with connection pooling and timeouts. Never use RestTemplate in production—it's blocking and doesn't scale.

application.properties (Performance tuning)PROPERTIES
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# JVM tuning
spring.jvm.args=-Xms2048m -Xmx2048m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+ParallelRefProcEnabled -XX:+DisableExplicitGC

# Connection pooling
spring.datasource.hikari.maximum-pool-size=50
spring.datasource.hikari.minimum-idle=10

# WebClient connection pool
spring.webclient.connect-timeout=5s
spring.webclient.response-timeout=30s
spring.codec.max-in-memory-size=10MB

# Thread pool for async operations
spring.task.execution.pool.core-size=10
spring.task.execution.pool.max-size=50
spring.task.execution.pool.queue-capacity=100
Output
Performance-tuned Spring Boot app with G1GC, connection pool, and thread pool settings.
🔥Pre-Warming for Flash Sales
📊 Production Insight
We reduced our Azure bill by 30% by using AKS spot node pools for non-critical batch jobs and reserved instances for the payment service. Combined with VPA recommendations, we cut memory over-provisioning by 40%.
🎯 Key Takeaway
Scale horizontally, use G1GC for low latency, and configure auto-scaling with custom metrics. Pre-warm instances for predictable traffic spikes.
● Production incidentPOST-MORTEMseverity: high

The Black Friday Connection Pool Meltdown

Symptom
Random HTTP 500 errors during peak traffic, followed by complete service unavailability. Logs showed 'HikariPool-1 - Connection is not available, request timed out after 30000ms'.
Assumption
We assumed default HikariCP settings (10 connections, 30-second timeout) would work fine since they worked on local MySQL. We also assumed Azure SQL would handle connections as fast as a local database.
Root cause
Azure SQL uses a gateway that adds 10-20ms per connection establishment. With default settings, during traffic spikes, connection acquisition requests queued up because the pool exhausted its 10 connections while waiting for new ones to be created. The 30-second timeout was too short for the cumulative latency.
Fix
Increased HikariCP maximumPoolSize to 50, set connectionTimeout to 60000ms, and enabled leakDetectionThreshold. Also added a connection validation query: SELECT 1. Deployed via App Service application settings without code change.
Key lesson
  • Always benchmark your connection pool against the actual managed database latency, not local dev settings
  • Set connectionTimeout higher than the sum of network latency + database response time under load
  • Monitor HikariCP metrics with Micrometer and Azure Application Insights to catch pool exhaustion early
Production debug guideStep-by-step debugging for common Azure deployment issues5 entries
Symptom · 01
HTTP 500 errors during traffic spikes
Fix
Check HikariCP metrics via /actuator/metrics/hikaricp.connections.pending. If >0, increase maximumPoolSize and connectionTimeout. Also check App Service's auto-scale logs for cooldown periods.
Symptom · 02
502 errors during AKS rolling update
Fix
Verify readiness probe returns 503 during shutdown. Check kubectl describe pod for probe failures. Increase spring.lifecycle.timeout-per-shutdown-phase to 60s and set preStop hook to sleep 5s.
Symptom · 03
Application Insights missing traces
Fix
Check connection string in application.properties. Verify applicationinsights-spring-boot-starter version (3.5.1+). Enable debug logging: logging.level.com.microsoft.applicationinsights=DEBUG. Check sampling percentage.
Symptom · 04
Slow database queries in production
Fix
Use Application Insights distributed tracing to identify slow database calls. Check HikariCP metrics for connection wait times. Review Azure SQL query performance insights in Azure Portal.
Symptom · 05
Pod OOMKilled in AKS
Fix
Check kubectl describe pod for resource limits. Use Vertical Pod Autoscaler in recommendation mode. Increase memory limits gradually. Monitor JVM heap usage via /actuator/metrics/jvm.memory.used.
★ Azure Deployment Quick Debug Cheat SheetImmediate actions for the top 5 Azure Spring Boot production issues
Connection pool timeout errors
Immediate action
Increase HikariCP maximumPoolSize to 50 and connectionTimeout to 60000ms
Commands
kubectl set env deployment/payment-service SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE=50 -n production
az webapp config appsettings set --resource-group my-rg --name my-app --settings SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE=50
Fix now
Restart the pod or App Service after changing settings. Monitor /actuator/health for recovery.
502 errors during rolling update+
Immediate action
Set readiness probe to return 503 during shutdown and increase shutdown timeout
Commands
kubectl patch deployment payment-service -n production -p '{"spec":{"template":{"spec":{"containers":[{"name":"payment-service","readinessProbe":{"httpGet":{"path":"/actuator/health/readiness","port":8080},"periodSeconds":1}}]}}}}'
kubectl set env deployment/payment-service SPRING_LIFETIME_TIMEOUT-PER-SHUTDOWN-PHASE=60s -n production
Fix now
Rollback to previous version if errors persist: kubectl rollout undo deployment/payment-service -n production
App Service OOM kills+
Immediate action
Increase JVM heap via JAVA_OPTS environment variable
Commands
az webapp config appsettings set --resource-group my-rg --name my-app --settings JAVA_OPTS="-Xmx4096m -Xms2048m"
az webapp restart --resource-group my-rg --name my-app
Fix now
If still OOM, scale up to higher App Service tier (P2v2 or P3v2) with more memory
Application Insights no data+
Immediate action
Verify connection string and enable debug logging
Commands
kubectl exec -it pod/payment-service-pod -- env | grep APPLICATIONINSIGHTS
kubectl set env deployment/payment-service LOGGING_LEVEL_COM_MICROSOFT_APPLICATIONINSIGHTS=DEBUG -n production
Fix now
If connection string missing, add via kubectl set env or Azure Portal app settings
AKS pod stuck in CrashLoopBackOff+
Immediate action
Check pod logs and resource limits
Commands
kubectl logs pod/payment-service-pod -n production --previous
kubectl describe pod/payment-service-pod -n production | grep -A5 -i 'limits\|requests'
Fix now
Increase memory limits in deployment.yaml and reapply: kubectl apply -f deployment.yaml
FeatureAzure App ServiceAzure Kubernetes Service (AKS)
Deployment ModelPaaS—upload JAR or containerContainer orchestration—manage pods and nodes
ScalingAuto-scale based on CPU/memory (5-min cooldown)HPA with custom metrics, KEDA for event-driven
NetworkingSimple, limited WebSocket supportFull Kubernetes networking, custom ingress, CNI
SSL/TLSAutomatic with custom domainsManual via cert-manager or Azure Key Vault
CostCheaper for low traffic (<10k req/s)Cost-effective at scale, spot node pools available
Operational OverheadLow—Azure patches OSMedium—manage node pools, updates, monitoring
Graceful ShutdownSimple via app settingsRequires probe coordination and preStop hooks
Secret ManagementKey Vault references in app settingsSecrets Store CSI driver or Key Vault FlexVolume
⚙ Quick Reference
8 commands from this guide
FileCommand / CodePurpose
pom.xml (Azure plugin section)Azure App Service
GracefulShutdownConfig.java@ConfigurationWhat the Official Docs Won't Tell You
DockerfileFROM eclipse-temurin:21-jdk-alpine AS buildDockerizing Your Spring Boot App for Azure
deployment.yamlapiVersion: apps/v1Deploying to Azure Kubernetes Service (AKS)
application-azure.propertiesspring.datasource.url=jdbc:sqlserver://myserver.database.windows.net:1433;databa...Configuring Azure-Specific Connection Pooling
.githubworkflowsdeploy-aks.ymlname: Deploy to AKSCI/CD with GitHub Actions for Azure Deployments
pom.xml (Application Insights dependency)Monitoring and Observability with Azure Application Insights
application.properties (Performance tuning)spring.jvm.args=-Xms2048m -Xmx2048m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+P...Scaling and Performance Tuning on Azure

Key takeaways

1
Choose Azure App Service for simplicity and AKS for control—match the deployment model to your operational capacity and scaling needs
2
Always tune HikariCP connection pools for Azure managed databases
larger pools, longer timeouts, and validation queries are mandatory
3
Implement graceful shutdown with health probe coordination for both App Service and AKS to avoid 502 errors during deployments
4
Use Azure Key Vault and managed identities for secret management—never hardcode credentials in configuration files
5
Set up Application Insights with Micrometer for production observability, including JVM metrics, connection pool metrics, and distributed tracing
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
How would you deploy a Spring Boot application to Azure App Service with...
Q02SENIOR
Explain how to configure Spring Boot's connection pool for Azure SQL in ...
Q03SENIOR
What are the key differences between deploying Spring Boot on Azure App ...
Q04SENIOR
How do you handle graceful shutdown in Spring Boot on AKS?
Q05SENIOR
What Azure-specific issues might you encounter with Spring Boot's defaul...
Q01 of 05SENIOR

How would you deploy a Spring Boot application to Azure App Service with zero downtime?

ANSWER
Use deployment slots in App Service. Deploy to a staging slot, run smoke tests, then swap with the production slot. Enable auto-swap with health check validation. Configure the staging slot with the same settings as production. For the app itself, ensure graceful shutdown is enabled (server.shutdown=graceful) and the health endpoint returns 503 during shutdown. Use WEBSITE_RUN_FROM_PACKAGE=1 for faster cold starts.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Should I use Azure App Service or AKS for my Spring Boot application?
02
How do I configure Spring Boot for Azure SQL connection pooling?
03
Why does my Spring Boot app get 502 errors during AKS rolling updates?
04
How do I monitor Spring Boot on Azure?
05
What's the best way to manage secrets for Spring Boot on Azure?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Spring Boot. Mark it forged?

7 min read · try the examples if you haven't

Previous
Spring Boot Gradle Plugin: Building and Packaging Spring Boot Applications
83 / 121 · Spring Boot
Next
OpenTelemetry Setup in Spring Boot: Distributed Tracing and Observability