Skip to main content
Run env zero self-hosted agents as ephemeral Azure Container Apps (ACA) Jobs that scale to zero when idle and spin up automatically when deployments are queued. Scaling is driven by KEDA, which is built into the Azure Container Apps managed environment, so no separate KEDA installation is required.
Self-hosted agents are available to Enterprise customers only. See pricing for details.

How It Works

KEDA polls the env zero agent queue state endpoint on a fixed interval and creates ACA job executions when deployment tasks are queued. Each container picks up one task, runs it to completion, and exits. When the queue is empty, no containers run.

Prerequisites

  • Azure CLI installed and authenticated (az login)
  • Azure subscription with Contributor access
  • An env zero agent pool with a valid agent access token. See Self-Hosted Agents Overview for how to create an agent pool and generate a secret.

Setup

1

Create a resource group

az group create -n env0-keda -l <YOUR_REGION>
2

Create a Container Apps environment

az containerapp env create \
  -n env0-keda-env \
  -g env0-keda \
  -l <YOUR_REGION>
This creates a managed environment with KEDA built in. No KEDA installation is needed.
3

Create the job configuration file

Save the following as env0-aca-job.yaml. Replace <YOUR_AGENT_ACCESS_TOKEN> with the agent access token generated for your agent pool, <YOUR_STATE_ENCRYPTION_KEY_BASE64> with a base64-encoded encryption key, and <X.Y.Z> with the agent version you want to pin to.
env0-aca-job.yaml
properties:
  configuration:
    triggerType: Event
    replicaTimeout: 3600
    replicaRetryLimit: 0
    secrets:
    - name: agent-token
      value: "<YOUR_AGENT_ACCESS_TOKEN>"
    eventTriggerConfig:
      parallelism: 1
      replicaCompletionCount: 1
      scale:
        minExecutions: 0
        maxExecutions: 10
        pollingInterval: 30
        rules:
        - name: env0-pending-tasks
          type: metrics-api
          metadata:
            url: "https://agent.api.env0.com/deployment-tasks/queue-state"
            valueLocation: "pendingCount"
            targetValue: "1"
            activationTargetValue: "0"
            authMode: "apiKey"
            method: "header"
            keyParamName: "Authorization"
          auth:
          - secretRef: agent-token
            triggerParameter: apiKey
  template:
    containers:
    - name: env0-agent
      image: ghcr.io/env0/deployment-agent:<X.Y.Z>
      command: ["/bin/sh", "-c"]
      args: ["update-ca-certificates; node --enable-source-maps start-agent.js;"]
      resources:
        cpu: 2
        memory: 4Gi
      env:
      - name: AGENT_ACCESS_TOKEN
        secretRef: agent-token
      - name: ENV0_STATE_ENCRYPTION_KEY
        value: "<YOUR_STATE_ENCRYPTION_KEY_BASE64>"
      - name: ENV0_STAGE
        value: "prod"
4

Create the job

az containerapp job create \
  -n env0-agent-job \
  -g env0-keda \
  --environment env0-keda-env \
  --yaml env0-aca-job.yaml
ENV0_STATE_ENCRYPTION_KEY must be a base64-encoded value and identical across all agent replicas. Mismatched keys make encrypted deployment state unreadable, which results in data loss.
Pin image to a specific agent version rather than :latest to avoid silent upgrades during a running deployment. See the env0 deployment agent releases for current tags.

Configuration Reference

KEDA Scaling Parameters

ParameterDefaultDescription
pollingInterval30Seconds between KEDA polls of the queue state endpoint.
maxExecutions10Maximum concurrent agent containers.
minExecutions0Set to 1 or higher to keep warm containers running.
replicaTimeout3600Maximum seconds a container can run before forced termination.
targetValue1Tasks per container. 1 means one container per pending task.
activationTargetValue0Metric threshold that activates scaling from zero.

Container Resources

ParameterDefaultDescription
cpu2CPU cores per container.
memory4GiMemory per container.

Environment Variables

VariableRequiredDescription
AGENT_ACCESS_TOKENYesAgent authentication token. Stored as an ACA secret and referenced by secretRef.
ENV0_STAGEYesDetermines which env zero API endpoint the agent connects to. Use prod for production.
ENV0_STATE_ENCRYPTION_KEYYesBase64-encoded encryption key for deployment state. Must be identical across all agent replicas.
DEPLOYMENT_LOGS_LOG_LEVELNoSet to debug for verbose agent logging.

Monitoring

List running and completed executions:
az containerapp job execution list -n env0-agent-job -g env0-keda -o table
View logs for a specific execution:
az containerapp job logs show \
  -n env0-agent-job -g env0-keda \
  --container env0-agent \
  --execution <EXECUTION_NAME> \
  --follow false
Stop a running execution:
az containerapp job stop \
  -n env0-agent-job -g env0-keda \
  --job-execution-name <EXECUTION_NAME>

Troubleshooting

No containers start when deployments are queued

Check the KEDA system logs for the Container Apps environment:
az containerapp env show -n env0-keda-env -g env0-keda \
  --query 'properties.appLogsConfiguration.logAnalyticsConfiguration.customerId' -o tsv

az monitor log-analytics query -w <WORKSPACE_ID> \
  --analytics-query "ContainerAppSystemLogs_CL | order by TimeGenerated desc | take 20" \
  -o table
Common causes:
  • api returned 401: the agent access token is incorrect or has been revoked. Generate a new secret and update the job configuration.
  • No KEDA activity logged: verify the job was created with triggerType: Event.

A container starts but fails immediately

Check the container logs for the specific error. Common causes:
  • Missing ENV0_STAGE: deployment commands build URLs from this variable and produce broken hosts when it is unset. Set it explicitly to prod for production.
  • Missing ENV0_STATE_ENCRYPTION_KEY: the agent exits immediately with an error.
  • Wrong AGENT_ACCESS_TOKEN: the agent starts but receives 401 on API calls.

Cleanup

To remove all resources created for this setup:
az group delete -n env0-keda -y

Next Steps