Deploying a Dockerized Java App to Azure Web App for Containers – A Detailed Guide
Containerization has transformed how we deploy applications, and when you pair Docker with Azure Web App for Containers (a flavor of Azure App Service), you get a powerful, managed platform to run your Java apps in the cloud. No need to manage servers, orchestrate clusters, or worry about scaling—Azure handles it all, letting you focus on your code. In this post, we’ll walk through deploying a Dockerized Java Spring Boot application to Azure Web App for Containers, step by step, with plenty of detail to ensure you grasp every nuance.
We’ll build a simple Spring Boot app, containerize it with Docker, push it to a registry, deploy it to Azure, and explore advanced options like environment variables, logging, and scaling. Whether you’re a Java veteran or just dipping your toes into cloud deployment, this guide has you covered. Let’s roll up our sleeves and get started!
Step 1: Create a Spring Boot Application
Our journey begins with a Java app. We’ll use Spring Boot—a popular framework for building robust web applications.
Set Up Your Environment:
Java: Install JDK 17 (e.g., OpenJDK or AdoptOpenJDK). Verify: java -version.
Maven: Install Maven 3.8.x or later (mvn -v).
Docker: Install Docker Desktop (docker.com) and check: docker --version.
Azure CLI: Install from here (az --version).
Generate the Spring Boot Project:
Use Spring Initializr (start.spring.io):
Project: Maven
Language: Java
Spring Boot: 3.4.x (latest stable)
Group: com.example
Artifact: java-azure-app
Dependencies: Spring Web.
Download the ZIP, extract it to a folder (e.g., java-azure-app), and open it in your IDE (e.g., IntelliJ or VS Code).
Add a REST Controller:
Edit src/main/java/com/example/javaazureapp/JavaAzureAppApplication.java:
package com.example.java_azure_app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class JavaAzureAppApplication { public static void main(String[] args) { SpringApplication.run(JavaAzureAppApplication.class, args); } // Homepage endpoint (already present) @GetMapping("/") public String home() { return "Welcome to the Java Azure Application!"; } // Health check endpoint @GetMapping("/health") public String healthCheck() { return "{\"status\": \"UP\", \"message\": \"Application is running normally\"}"; } // Environment variable demo endpoint @GetMapping("/env-demo") public String envDemo() { String envVar = System.getenv("DEMO_ENV_VAR"); // Replace DEMO_ENV_VAR with your env variable name return String.format("Environment variable DEMO_ENV_VAR value: %s", envVar != null ? envVar : "Not set"); } @GetMapping("/hello") public String hello(@RequestParam(value = "name", defaultValue = "World") String name) { return String.format("Hello, %s!", name); } @GetMapping("/api/users/{id}") public User getUser(@PathVariable("id") Long id) { // This is a simple example - in a real app, you'd fetch from a database return new User(id, "User " + id, "user" + id + "@example.com"); } // A simple model class for the above endpoint public static class User { private Long id; private String name; private String email; public User(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } // Getters and setters (needed for proper JSON serialization) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } }
This adds three endpoints: a homepage, a health check, and an environment variable demo (we’ll use this later)
Build and Test Locally:
In the project root:
# First build the application mvn clean package # Then run the built JAR file java -jar target/java-azure-app-0.0.1-SNAPSHOT.jar
Visit localhost:8080, /health, and /env (the last one will be blank for now). Stop with Ctrl+C.
Step 2: Dockerize the Application
Next, we’ll containerize the app using Docker to ensure consistency across environments.
Create a Dockerfile:
In the java-azure-app directory, create Dockerfile:
dockerfile
# Stage 1: Build the app FROM maven:3.8.6-openjdk-17-slim AS build WORKDIR /app COPY pom.xml . COPY src ./src RUN mvn clean package -DskipTests # Stage 2: Run the app FROM openjdk:17-jdk-slim WORKDIR /app COPY --from=build /app/target/java-azure-app-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
Why Multi-Stage?:
First stage (build): Compiles the app with Maven.
Second stage: Uses a slim runtime image, copying only the JAR, reducing the final image size.
Add a .dockerignore:
Create .dockerignore to exclude unnecessary files:
target *.md .mvn mvnw mvnw.cmd
This speeds up builds and keeps the image lean.
Build the Docker Image:
docker build -t java-azure-app:latest .
Watch the layers build. Errors? Check for typos or missing files (e.g., pom.xml).
.
Test the Container Locally:
docker run -p 8080:8080 java-azure-app:latest
Open localhost:8080 and test all endpoints. Stop with Ctrl+C.
Step 3: Push the Image to a Container Registry
Azure Web App for Containers pulls images from a registry. We’ll use Docker Hub, but Azure Container Registry (ACR) is an alternative.
Log In to Docker Hub:
docker login
- Use your Docker Hub credentials (create an account if needed).
Tag the Image:
Replace <your-dockerhub-username>:
docker tag java-azure-app:latest <your-dockerhub-username>/java-azure-app:latest
Push to Docker Hub:
docker push <your-dockerhub-username>/java-azure-app:latest
Verify it’s uploaded by checking your Docker Hub account online.
Optional: you can Use ACR( azure container registry):
how to Create an ACR:
az acr create --resource-group JavaAppRG --name myacr123 --sku Basic
Log in: az acr login --name myacr123.
Tag and push: docker tag java-azure-app:latest myacr123.azurecr.io/java-azure-app:latest and docker push.
Step 4: Deploy to Azure Web App for Containers
Now, let’s deploy using Azure CLI for precision and automation.
Authenticate with Azure CLI:
az login az account set --subscription "<subscription-id>" # If multiple subscriptions
Create a Resource Group:
az group create --name JavaAppRG --location westeurope
--location: Choose a region close to your users (list with az account list-locations).
Create an App Service Plan:
az appservice plan create --name JavaAppPlan --resource-group JavaAppRG --sku B1 --is-linux
--sku B1: Basic tier (1.75 GB RAM, free-tier eligible). Options: S1, P1V2 for production.
Create the Web App:
Use a unique <your-app-name>:
az webapp create --resource-group JavaAppRG --plan JavaAppPlan --name <your-app-name> --deployment-container-image-name <your-dockerhub-username>/java-azure-app:latest
Configure the Container Port:
az webapp config appsettings set --resource-group JavaAppRG --name <your-app-name> --settings WEBSITES_PORT=8080
Spring Boot uses 8080; this aligns Azure with your app.
Set Environment Variables:
Add a variable for the /env endpoint:
az webapp config appsettings set --resource-group JavaAppRG --name <your-app-name> --settings APP_ENV="Production"
Restart the App:
az webapp restart --resource-group JavaAppRG --name <your-app-name>
STEP 5: Verify and Troubleshoot
Let’s ensure it’s running smoothly.
Get the URL:
az webapp show --resource-group JavaAppRG --name <your-app-name> --query "defaultHostName" --output tsv
Example: https://<your-app-name>.azurewebsites.net.
Test Endpoints:
Browser or curl:
curl https://<your-app-name>.azurewebsites.net/ curl https://<your-app-name>.azurewebsites.net/health curl https://<your-app-name>.azurewebsites.net/env
Expect: "Welcome...", "Status: Healthy", and "Environment: Production".
View Logs:
az webapp log tail --resource-group JavaAppRG --name <your-app-name>
- Look for Spring Boot startup logs or errors (e.g., port binding issues).
Check Container Settings:
az webapp config container show --resource-group JavaAppRG --name <your-app-name> --output table
STEP 6: Advanced Configuration and Management (optional)
level up with updates, scaling, and monitoring.
Update the App:
Edit JavaAzureAppApplication.java (e.g., change "Welcome" to "Hello, Azure!").
Rebuild and push:
mvn clean package docker build -t <your-dockerhub-username>/java-azure-app:latest . docker push <your-dockerhub-username>/java-azure-app:latest
Update Azure:
az webapp config container set --resource-group JavaAppRG --name <your-app-name> --docker-custom-image-name <your-dockerhub-username>/java-azure-app:latest az webapp restart --resource-group JavaAppRG --name <your-app-name>
Add a Custom Domain (Optional):
Map a domain (e.g., myapp.com):
az webapp config hostname add --resource-group JavaAppRG --webapp-name <your-app-name> --hostname "www.myapp.com"
Enable Continuous Deployment:
- In the Azure Portal: Deployment Center > Docker Hub > Enable CI/CD.
Scale the App:
Upgrade plan:
az appservice plan update --name JavaAppPlan --resource-group JavaAppRG --sku S1
Auto-scale:
az monitor autoscale create --resource-group JavaAppRG --resource JavaAppPlan --resource-type Microsoft.Web/serverfarms --name JavaAutoScale --min-count 1 --max-count 4 --count 1 az monitor autoscale rule create --resource-group JavaAppRG --autoscale-name JavaAutoScale --condition "CpuPercentage > 70 avg 5m" --scale out 1
Monitor Performance:
Check CPU usage:
az monitor metrics list --resource /subscriptions/<subscription-id>/resourceGroups/JavaAppRG/providers/Microsoft.Web/sites/<your-app-name> --metric "CpuTime" --start-time $(date -u -d "5 minutes ago" '+%Y-%m-%dT%H:%M:%SZ') --output table
STEP 7: Clean Up
Avoid unexpected costs:
az group delete --name JavaAppRG --yes --no-wait
Conclusion
You’ve just deployed a Dockerized Java Spring Boot app to Azure Web App for Containers with full control over the process. From building the app to scaling it in the cloud, this setup leverages Docker’s portability and Azure’s managed power.
WATCH OUT FOR SECOND PART OF THIS GUIDE, WHERE I WILL BE DEPLOYING JAVA APP WITH MYSQL DATABASE OR COSMOS DB. Till then Happy coding!!!!!