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.
20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.
- ✓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)
• 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
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/).
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.
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.
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.
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.
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.
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.
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.
The Black Friday Connection Pool Meltdown
- 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
kubectl set env deployment/payment-service SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE=50 -n productionaz webapp config appsettings set --resource-group my-rg --name my-app --settings SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE=50| File | Command / Code | Purpose |
|---|---|---|
| pom.xml (Azure plugin section) | Azure App Service | |
| GracefulShutdownConfig.java | @Configuration | What the Official Docs Won't Tell You |
| Dockerfile | FROM eclipse-temurin:21-jdk-alpine AS build | Dockerizing Your Spring Boot App for Azure |
| deployment.yaml | apiVersion: apps/v1 | Deploying to Azure Kubernetes Service (AKS) |
| application-azure.properties | spring.datasource.url=jdbc:sqlserver://myserver.database.windows.net:1433;databa... | Configuring Azure-Specific Connection Pooling |
| .github | name: Deploy to AKS | CI/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
Interview Questions on This Topic
How would you deploy a Spring Boot application to Azure App Service with zero downtime?
Frequently Asked Questions
20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.
That's Spring Boot. Mark it forged?
7 min read · try the examples if you haven't