Deploying a Dockerized Java App to Azure Web App for Containers – A Detailed Guide

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.

  1. 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).

  2. 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).

  3. 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)

  4. 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.

  1. 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.

  2. Add a .dockerignore:

    • Create .dockerignore to exclude unnecessary files:

        target
        *.md
        .mvn
        mvnw
        mvnw.cmd
      
    • This speeds up builds and keeps the image lean.

  3. 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).

      .

  4. Test the Container Locally:

     docker run -p 8080:8080 java-azure-app:latest
    

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.

  1. Log In to Docker Hub:

     docker login
    
    • Use your Docker Hub credentials (create an account if needed).
  2. Tag the Image:

    • Replace <your-dockerhub-username>:

        docker tag java-azure-app:latest <your-dockerhub-username>/java-azure-app:latest
      
  3. Push to Docker Hub:

     docker push <your-dockerhub-username>/java-azure-app:latest
    
    • Verify it’s uploaded by checking your Docker Hub account online.

  4. 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.

  1. Authenticate with Azure CLI:

     az login
     az account set --subscription "<subscription-id>"  # If multiple subscriptions
    

  2. 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).

  3. 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.

  4. 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
      

  1. 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.

  2. 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"
      

  1. 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.

  1. Get the URL:

     az webapp show --resource-group JavaAppRG --name <your-app-name> --query "defaultHostName" --output tsv
    
  2. 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".

  3. 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).
  4. 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.

  1. 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>
      
  2. 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"
      
  3. Enable Continuous Deployment:

    • In the Azure Portal: Deployment Center > Docker Hub > Enable CI/CD.
  4. 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
      
  5. 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!!!!!