This guide deploys Twilio Mixologist to Azure Container Apps with public HTTPS ingress so Twilio can call /webhooks/messaging.
- Azure subscription
- Azure CLI installed
- Twilio credentials and service SIDs
.env.localcreated fromsample.env
Create your local environment file:
cp sample.env .env.localEdit .env.local and fill in the required Twilio values:
TWILIO_ACCOUNT_SID="ACxxxxx"
TWILIO_API_KEY="SKxxxxx"
TWILIO_API_SECRET="xxxxx"
TWILIO_VERIFY_SERVICE_SID="VAxxxxx"
TWILIO_SYNC_SERVICE_SID="ISxxxxx"
TWILIO_MESSAGING_SERVICE_SID="MGxxxxx"The deploy script uses .env.local for both build-time values, such as NEXT_PUBLIC_*, and runtime environment variables.
az loginThe script automatically installs or updates the Azure Container Apps CLI extension.
Run the deploy script from the repository root:
./deploy.shThe script runs non-interactively and uses these defaults unless overridden via environment variables:
- Azure resource group:
AZURE_RESOURCE_GROUP(defaultrg-twilio-mixologist) - Azure region:
AZURE_LOCATION(defaultnortheurope) - Azure Container Registry name:
AZURE_ACR_NAME(default derived from the app name) - Container App name:
AZURE_CONTAINER_APP_NAME(defaulttwilio-mixologist) - Container Apps environment name:
AZURE_CONTAINER_ENV_NAME(defaultcae-twilio-mixologist)
The script creates or updates:
- Resource group
- Azure Container Registry
- Log Analytics workspace
- Container Apps environment
- Container App with external ingress on port
3000
Sensitive values such as TWILIO_API_SECRET, BASIC_AUTH_PASSWORD, and SEGMENT_PROFILE_KEY are configured as Container App secrets. The app is pinned to one replica because event runtime state should not fan out unexpectedly.
When deployment finishes, the script prints:
App URL: https://<your-app-fqdn>
Twilio webhook: https://<your-app-fqdn>/webhooks/messaging
In Twilio Console, configure your WhatsApp or SMS sender incoming message webhook:
https://<your-app-fqdn>/webhooks/messaging
Open the app:
curl -i "https://<your-app-fqdn>/"Check the incoming webhook route:
curl -i "https://<your-app-fqdn>/webhooks/messaging"Stream logs:
az containerapp logs show \
--resource-group "<your-resource-group>" \
--name "<your-container-app-name>" \
--followCheck ingress:
az containerapp show \
--resource-group "<your-resource-group>" \
--name "<your-container-app-name>" \
--query "properties.configuration.ingress.{external:external,fqdn:fqdn,targetPort:targetPort}" \
--output json.github/workflows/deploy.yml runs ./deploy.sh automatically on every push to main
(i.e. whenever a PR is merged), authenticating to Azure via OIDC instead of az login.
This repo's Actions policy only allows GitHub-owned actions, so the workflow performs
the OIDC token exchange by hand rather than via azure/login. For it to work, an Azure
AD App Registration needs a federated credential trusting this repo's main branch
(Certificates & secrets → Federated credentials, entity type "Branch", branch main),
with the Contributor role on the target subscription/resource group. Then set these
as repo secrets:
AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_SUBSCRIPTION_ID
The workflow also writes a .env.local file from repo secrets/variables before calling
deploy.sh, mirroring the variables in sample.env. Add any missing ones (e.g.
OPENAI_API_KEY, SEGMENT_PROFILE_KEY, BADGE_API_KEY) as repo secrets, or as repo
variables for non-sensitive values, so they're picked up the same way.
Expected ingress values:
external:truetargetPort:3000
Before deploying, you can build and run the container locally:
docker compose --env-file .env.local build
docker compose --env-file .env.local upOpen http://localhost:3000.
Set these before running ./deploy.sh if you do not want to use the prompts:
export AZURE_RESOURCE_GROUP="rg-twilio-mixologist"
export AZURE_LOCATION="northeurope"
export AZURE_ACR_NAME="twiliomixologistacr"
export AZURE_CONTAINER_APP_NAME="twilio-mixologist"
export AZURE_CONTAINER_ENV_NAME="cae-twilio-mixologist"
export AZURE_LOG_ANALYTICS_WORKSPACE="logs-twilio-mixologist"
export IMAGE_NAME="twilio-mixologist"
export IMAGE_TAG="latest"
export ENV_FILE=".env.local"Good region choices:
- UK event:
uksouth - Germany or EU-first event:
northeurope
If the ACR build fails with Request failed with status code 401 or Failed to fetch Templates, check that .env.local contains valid Twilio credentials.
If Azure blocks resource creation with RequestDisallowedByPolicy and mentions tags['created_by'], set AZURE_USER before running the script:
export AZURE_USER="your.name@example.com"If the deployed URL shows a Container Apps 404 page, confirm ingress is external and the target port is 3000. To repair it:
az containerapp ingress enable \
--resource-group "<your-resource-group>" \
--name "<your-container-app-name>" \
--type external \
--target-port 3000