Edit

Share via


Deploy to Azure Container Apps using the Azure Developer CLI

The Azure Developer CLI (azd) supports deploying both Azure Container Apps and Azure Container App Jobs. For Azure Container Apps, azd offers two deployment strategies:

  • Image-based strategy. Separates container app configuration updates from image deployments.
  • Revision-based strategy. Combines both into a single deployment and supports advanced rollout patterns.

The following sections explain both strategies, along with how to deploy Container App Jobs.

Image-based deployment strategy

In this strategy, the container app configuration is created and updated during azd provision, while the container image is updated during azd deploy.

  • The container app definition (resources, environment variables, health probes, and so on) resides in a Bicep module applied during provisioning.
  • Only the container image reference (containers[0].image) changes during deployment.

Revision behavior

Each change to the app configuration or image triggers a new revision:

Step Command Applies changes to Notes
1 azd provision Environment variables, resources, mounts, probes, load balancers Creates a new revision
2 azd deploy Container image Creates another revision

Each revision allocates additional replicas in the Container Apps environment, which might temporarily increase resource usage and cost.

Note

Advanced rollout patterns, such as blue-green or canary, aren't supported in this strategy.

Configure image-based deployments

To ensure that azd provision updates an existing container app without overwriting the latest deployed image, perform an upsert operation. This pattern is implemented by the AVM container-app-upsert module and consists of two steps:

  1. In your main.parameters.json, define a parameter that references the azd-provided variable SERVICE_{NAME}_RESOURCE_EXISTS. This variable gets set automatically by azd at provision time to indicate whether the resource already exists.

    {
      "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": {
        "environmentName": {
          "value": "${AZURE_ENV_NAME}"
        },
        "location": {
          "value": "${AZURE_LOCATION}"
        },
        // ... other parameters
        "apiExists": {
          "value": "${SERVICE_API_RESOURCE_EXISTS}"
        }
      }
    }
    
  2. In your Bicep file, reference the exists parameter to control whether the container app should be created or updated. The container-app-upsert module encapsulates this logic internally.

    @description('Indicates whether the container app resource already exists.')
    param apiExists bool
    
    module api 'br/public:avm/ptn/azd/container-app-upsert:0.1.2' = {
      name: 'api'
      params: {
        name: 'my-api'
        location: location
        containerAppsEnvironmentName: containerAppsEnvironment.name
        containerRegistryName: containerRegistry.name
        imageName: !empty(apiImageName) ? apiImageName : ''
        exists: apiExists
        env: [
          {
            name: 'MONGODB_CONNECTION_STRING'
            value: mongodb.outputs.connectionString
          }
        ]
        targetPort: 3100
      }
    }
    

    This approach allows azd provision to upsert (update if exists, create if not) the container app resource safely without manual checks.

    Tip

    Keep the apiVersion in azure.yaml aligned with the Bicep module's apiVersion for Microsoft.App/containerApps to avoid mismatches.

Revision-based deployment strategy

In this strategy, both the container app definition and image are deployed together during azd deploy.

  • The container app configuration resides in a dedicated Bicep module applied during deployment.

  • Changes to environment variables, images, resources, and load-balancing settings are rolled out as a single revision.

    Tip

    This strategy supports blue-green, canary, and other advanced rollout patterns.

Configure revision-based deployments

  1. Define the container app deployment by creating an infra file for your service, such as infra/api.bicep. You can define your container app by using the AVM-based module or by defining the resource directly:

    @description('Unique environment name used for resource naming.')
    param environmentName string
    
    @description('Primary location for all resources.')
    param location string
    
    param containerRegistryName string
    param containerAppsEnvironmentName string
    param imageName string
    param identityId string
    
    resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
      name: containerRegistryName
    }
    
    resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-03-01' existing = {
      name: containerAppsEnvironmentName
    }
    
    module api 'br/public:avm/res/app/container-app:0.8.0' = {
      name: 'api'
      params: {
        name: 'api'
        ingressTargetPort: 80
        scaleMinReplicas: 1
        scaleMaxReplicas: 10
        containers: [
          {
            name: 'main'
            image: imageName
            resources: {
              cpu: json('0.5')
              memory: '1.0Gi'
            }
          }
        ]
        managedIdentities: {
          systemAssigned: false
          userAssignedResourceIds: [identityId]
        }
        registries: [
          {
            server: containerRegistry.properties.loginServer
            identity: identityId
          }
        ]
        environmentResourceId: containerAppsEnvironment.id
        location: location
        tags: {
          'azd-env-name': environmentName
          'azd-service-name': 'api'
        }
      }
    }
    
  2. Provide parameters at deploy time by creating a parameters file (e.g. api.parameters.json):

    {
      "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": {
        "environmentName": { "value": "${AZURE_ENV_NAME}" },
        "location": { "value": "${AZURE_LOCATION}" },
        "containerRegistryName": { "value": "${AZURE_CONTAINER_REGISTRY_NAME}" },
        "containerAppsEnvironmentName": { "value": "${AZURE_CONTAINER_ENVIRONMENT_NAME}" },
        "imageName": { "value": "${SERVICE_API_IMAGE_NAME}" },
        "identityId": { "value": "${SERVICE_API_IDENTITY_ID}" }
      }
    }
    

    Note

    SERVICE_API_IMAGE_NAME is dynamically set during deploy and isn't part of the provision outputs.

    When you run azd deploy, the container app revision is applied using the resource definition above.

    Tip

    Pass any additional outputs from azd provision as parameters to azd deploy if your container app references other provisioned resources.

Comparison summary

Aspect Image-based Revision-based
Update command azd provision + azd deploy azd deploy only
Rollout type Two revisions Single revision
Rollout control Managed by azd Configurable (blue-green, canary)
Use case Simple environments Advanced deployments
Container app definition location Provision-time Bicep Deploy-time Bicep

Deploy Container App Jobs

In addition to Container Apps, azd supports deploying Azure Container App Jobs (Microsoft.App/jobs). Container App Jobs are designed for tasks that run to completion, such as batch processing, scheduled tasks, or event-driven work.

Note

Container App Jobs use the same host: containerapp setting in azure.yaml. No new host type is required. The Bicep template determines whether azd provisions a Container App or a Container App Job based on the resource type you define.

How it works

When azd discovers a job resource tagged with azd-service-name, it:

  1. Builds and pushes the Docker image to Azure Container Registry (same as Container Apps).
  2. Updates the job's container image by calling the Container App Jobs API instead of the Container Apps API.
  3. Returns empty endpoints, because jobs have no ingress.

Configure a Container App Job deployment

  1. Define the azure.yaml file for your job service. Use host: containerapp and language: docker:

    name: myapp
    services:
      job:
        host: containerapp
        language: docker
        project: ./src/job
        docker:
          path: ./Dockerfile
          context: .
    
  2. Create a Bicep module that provisions a Microsoft.App/jobs resource. Tag the resource with azd-service-name so azd can discover it. The parameters and existing resource references (container registry, managed environment, identity) follow the same pattern as the Container Apps examples above:

    resource job 'Microsoft.App/jobs@2025-02-02-preview' = {
      name: 'job'
      location: location
      tags: {
        'azd-env-name': environmentName
        'azd-service-name': 'job'
      }
      properties: {
        environmentId: containerAppsEnvironment.id
        configuration: {
          replicaTimeout: 300
          replicaRetryLimit: 1
          triggerType: 'Manual'
          registries: [
            {
              server: containerRegistry.properties.loginServer
              identity: identityId
            }
          ]
        }
        template: {
          containers: [
            {
              image: imageName
              name: 'main'
              resources: {
                cpu: json('0.5')
                memory: '1.0Gi'
              }
            }
          ]
        }
      }
      identity: {
        type: 'UserAssigned'
        userAssignedIdentities: {
          '${identityId}': {}
        }
      }
    }
    

    Important

    The azd-service-name tag value must match the service name in your azure.yaml file. This tag is how azd associates the provisioned resource with your service.

  3. Run azd up to provision and deploy. The CLI automatically detects that the tagged resource is a Container App Job and handles the deployment accordingly.

Key differences from Container Apps

Aspect Container Apps Container App Jobs
Resource type Microsoft.App/containerApps Microsoft.App/jobs
Ingress/endpoints Supports HTTP ingress No ingress (empty endpoints)
Execution model Long-running services Run-to-completion tasks
Trigger types Request-driven Manual, scheduled, or event-driven
Host setting in azure.yaml host: containerapp host: containerapp

Additional resources