O Azure App Service é o caminho mais rápido para levar código .NET a uma URL de produção em funcionamento se você já está no ecossistema Microsoft. Ele cuida do patching do sistema operacional, atualizações de runtime, certificados TLS e balanceamento de carga — você só precisa implantar sua aplicação.
Pré-requisitos
# Instalar o Azure CLI
winget install Microsoft.AzureCLI
# Fazer login
az loginVersões recentes da Azure CLI mudaram o fluxo de login (encontrei isso na 2.88.0):
az login agora lista todas as assinaturas que sua conta pode acessar e pede para você
escolher uma ali mesmo, em vez de assumir uma silenciosamente. Então o az account set
abaixo não faz mais parte do ritual do primeiro login — você só precisa dele para trocar
de assinatura mais tarde.
# Definir sua assinatura (se você tiver várias e quiser trocar depois)
az account set --subscription "My Subscription"Criando um App Service pela CLI
# 1. Criar um grupo de recursos (contêiner lógico para recursos relacionados)
az group create \
--name myapp-rg \
--location eastus
# 2. Criar um plano do App Service (define o nível de computação)
az appservice plan create \
--name myapp-plan \
--resource-group myapp-rg \
--sku B1 \
--is-linux
# 3. Criar a aplicação web apontando para o .NET 10
az webapp create \
--name myapp-12345 \ # Deve ser globalmente único
--resource-group myapp-rg \
--plan myapp-plan \
--runtime "DOTNETCORE:10.0"
# Sua aplicação já está disponível em: https://myapp-12345.azurewebsites.netUma armadilha que vale conhecer: --runtime usa a forma com dois-pontos (DOTNETCORE:10.0), mas o comando que lista o que sua região oferece imprime a forma com barra vertical — não os compare literalmente em um script:
# Imprime p. ex. DOTNETCORE|10.0 (com suporte até 2028-12-01), DOTNETCORE|8.0, ...
az webapp list-runtimes --os linux
Níveis de preços
Os preços abaixo não são de memória — vêm da API pública Retail Prices do Azure (Linux, East US, pagamento por uso, consultados em 2026-08-18; mensal ≈ por hora × 730):
| Nível | Por hora | ~Mensal | RAM | vCPU | Principais recursos |
|---|---|---|---|---|---|
| F1 Free | $0 | $0 | 1 GB | Compartilhado | 60 CPU-min/dia, sem TLS de domínio personalizado |
| B1 Basic | $0.017 | ~$12 | 1,75 GB | 1 | Always On, TLS de domínio personalizado |
| B2 Basic | $0.034 | ~$25 | 3,5 GB | 2 | — |
| B3 Basic | $0.067 | ~$49 | 7 GB | 4 | — |
| S1 Standard | $0.095 | ~$69 | 1,75 GB | 1 | Slots de implantação, autoescala |
| S2 Standard | $0.190 | ~$139 | 3,5 GB | 2 | — |
| P0v3 Premium | $0.0775 | ~$57 | 4 GB | 1 | Slots, autoescala, redundância de zona |
| P1v3 Premium | $0.155 | ~$113 | 8 GB | 2 | — |
| P2v3 Premium | $0.310 | ~$226 | 16 GB | 4 | — |
Reproduza (ou consulte sua própria região) com uma única requisição — sem autenticação:
curl -sG "https://prices.azure.com/api/retail/prices" \
--data-urlencode "\$filter=serviceName eq 'Azure App Service' and armRegionName eq 'eastus' and priceType eq 'Consumption'"Para aplicações de produção pequenas, o B1 ($12/mês) é excelente. Mas olhe com atenção antes de escolher Standard pelos slots de implantação: **o P0v3 ($57) é mais barato que o S1 (~$69)** com mais RAM, hardware mais novo e tudo o que o Standard oferece. Compare Premium v3 com Standard na sua região antes de assumir que Standard é o nível econômico com slots.
Implantando sua aplicação
Método 1: ZIP Deploy (o mais rápido)
# Compilar e publicar
dotnet publish -c Release -o ./publish
# Criar o ZIP
cd ./publish && zip -r ../app.zip . && cd ..
# Implantar
az webapp deploy \
--resource-group myapp-rg \
--name myapp-12345 \
--src-path app.zip \
--type zipEsse caminho exato — grupo, plano, app, app settings, deploy zip — está automatizado de ponta a ponta em
samples/azure-app-service-dotnet
(uma API mínima cuja resposta comprova cada afirmação de configuração deste artigo). Duas coisas medidas ao executá-lo, para calibrar expectativas:
- A implantação completa levou ~2,5 minutos, e quase tudo foi a fase "Starting the site" do Kudu depois do upload — o zip em si foi aceito em cerca de um segundo. Não assuma que a implantação travou ao passar dos 90 segundos.
- O Git Bash no Windows não inclui
zip— e não recorra aoCompress-Archive. OCompress-Archivedo PowerShell grava os nomes das entradas com barra invertida, e o rsync do Kudu no Linux os rejeita (Invalid argument (22)) assim que a saída de publish contém uma subpasta; uma saída plana sobrevive por sorte, e é por isso que esse bug se esconde. Use o bsdtar embutido do Windows:C:\Windows\System32\tar.exe -a -cf app.zip -C publish *produz um zip correto com barras normais.
Método 2: GitHub Actions (recomendado para produção)
Primeiro, baixe o perfil de publicação pelo portal ou pela CLI:
az webapp deployment list-publishing-profiles \
--resource-group myapp-rg \
--name myapp-12345 \
--xml > publish-profile.xmlAdicione o conteúdo como um segredo do GitHub com o nome AZURE_WEBAPP_PUBLISH_PROFILE, em seguida:
# .github/workflows/deploy.yml
name: Deploy to Azure App Service
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
- name: Publish
run: dotnet publish -c Release -o ${{ github.workspace }}/publish
- name: Deploy to Azure
uses: azure/webapps-deploy@v3
with:
app-name: myapp-12345
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: ${{ github.workspace }}/publishMétodo 3: Azure CLI pelo GitHub Actions (autenticação OIDC — sem segredos)
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy
run: |
cd ./publish && zip -r ../app.zip . && cd ..
az webapp deploy \
--resource-group myapp-rg \
--name myapp-12345 \
--src-path ./app.zip \
--type zipVariáveis de ambiente e configuração
Definindo as configurações da aplicação
As configurações da aplicação tornam-se variáveis de ambiente dentro do seu app, substituindo o appsettings.json:
# Definir valores individuais
az webapp config appsettings set \
--resource-group myapp-rg \
--name myapp-12345 \
--settings \
ASPNETCORE_ENVIRONMENT=Production \
FeatureFlags__NewCheckout=true
# Definir a partir de um arquivo JSON
az webapp config appsettings set \
--resource-group myapp-rg \
--name myapp-12345 \
--settings @app-settings.jsonO sublinhado duplo (FeatureFlags__NewCheckout) corresponde ao separador de hierarquia : do appsettings.json, e um App Setting vence a mesma chave no appsettings.json. A aplicação de exemplo implantada acima demonstra essa precedência — seu appsettings.json diz "from appsettings.json", mas a resposta implantada mostra o valor definido via CLI:

Strings de conexão
As strings de conexão recebem tratamento especial — são acessíveis via ConnectionStrings:Name na configuração do .NET:
az webapp config connection-string set \
--resource-group myapp-rg \
--name myapp-12345 \
--connection-string-type SQLAzure \
--settings DefaultConnection="Server=myserver.database.windows.net;..."// Na sua aplicação, lê primeiro do App Settings do Azure, depois do appsettings.json
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");Usando o Key Vault para segredos
Referencie segredos do Key Vault sem armazená-los no App Settings:
# Criar o Key Vault
az keyvault create --name myapp-kv --resource-group myapp-rg --location eastus
# Armazenar um segredo
az keyvault secret set --vault-name myapp-kv --name DbPassword --value "s3cr3t!"
# Habilitar identidade gerenciada atribuída pelo sistema na aplicação web
az webapp identity assign --resource-group myapp-rg --name myapp-12345
# Obter o ID do principal
principalId=$(az webapp identity show --resource-group myapp-rg --name myapp-12345 --query principalId -o tsv)
# Conceder à aplicação web acesso de leitura aos segredos do Key Vault
az keyvault set-policy --name myapp-kv --object-id $principalId --secret-permissions get listReferencie os segredos no App Settings usando referências do Key Vault:
az webapp config appsettings set \
--resource-group myapp-rg \
--name myapp-12345 \
--settings "DbPassword=@Microsoft.KeyVault(VaultName=myapp-kv;SecretName=DbPassword)"Slots de implantação
Os slots de implantação fornecem ambientes de staging e trocas sem tempo de inatividade. Disponíveis nos níveis S1 e superiores.
# Criar um slot de staging
az webapp deployment slot create \
--name myapp-12345 \
--resource-group myapp-rg \
--slot staging
# Implantar no staging (não em produção)
az webapp deploy \
--resource-group myapp-rg \
--name myapp-12345 \
--slot staging \
--src-path app.zip \
--type zip
# Testar o staging em: https://myapp-12345-staging.azurewebsites.net
# Trocar staging com produção (quase sem tempo de inatividade)
az webapp deployment slot swap \
--resource-group myapp-rg \
--name myapp-12345 \
--slot staging \
--target-slot production
# Reverter (trocar novamente)
az webapp deployment slot swap \
--resource-group myapp-rg \
--name myapp-12345 \
--slot staging \
--target-slot productionConfigure definições específicas do slot (como strings de conexão) com o sinalizador "slot setting". Essas definições NÃO são trocadas junto com o slot — o slot de staging mantém sua conexão com o banco de dados de staging após a troca.
# Marcar uma configuração como específica do slot (não será trocada)
az webapp config appsettings set \
--resource-group myapp-rg \
--name myapp-12345 \
--slot staging \
--slot-settings ConnectionStrings__DefaultConnection="staging-db-connection"Escalabilidade
Escalonamento manual (nível Basic)
# Escalar verticalmente (alterar o tamanho da VM)
az appservice plan update \
--name myapp-plan \
--resource-group myapp-rg \
--sku S2
# Escalar horizontalmente (adicionar instâncias) — o nível Basic suporta apenas escalonamento horizontal manual
az appservice plan update \
--name myapp-plan \
--resource-group myapp-rg \
--number-of-workers 3Autoescala (nível Standard e superiores)
# Habilitar autoescala para o plano do App Service
az monitor autoscale create \
--resource-group myapp-rg \
--resource myapp-plan \
--resource-type Microsoft.Web/serverFarms \
--name myapp-autoscale \
--min-count 1 \
--max-count 5 \
--count 1
# Adicionar regra de escala para fora (adicionar instância quando CPU > 70%)
az monitor autoscale rule create \
--resource-group myapp-rg \
--autoscale-name myapp-autoscale \
--condition "CpuPercentage > 70 avg 5m" \
--scale out 1
# Adicionar regra de escala para dentro (remover instância quando CPU < 30%)
az monitor autoscale rule create \
--resource-group myapp-rg \
--autoscale-name myapp-autoscale \
--condition "CpuPercentage < 30 avg 10m" \
--scale in 1Configurando o Always-On
Por padrão, as aplicações no nível Basic e superiores são encerradas após 20 minutos de inatividade. Habilite o Always On:
az webapp config set \
--resource-group myapp-rg \
--name myapp-12345 \
--always-on trueO Always On não está disponível no nível F1 Free. Se você estiver no nível Free e enfrentar cold starts, faça upgrade para pelo menos o nível B1.
Domínio personalizado e TLS
# Adicionar um domínio personalizado
az webapp config hostname add \
--resource-group myapp-rg \
--webapp-name myapp-12345 \
--hostname www.yourdomain.com
# Criar um certificado TLS gerenciado (gratuito)
az webapp config ssl create \
--resource-group myapp-rg \
--name myapp-12345 \
--hostname www.yourdomain.com
# Vincular o certificado
az webapp config ssl bind \
--resource-group myapp-rg \
--name myapp-12345 \
--certificate-thumbprint <thumbprint-from-previous-command> \
--ssl-type SNIMonitoramento com Application Insights
# Criar o Application Insights
az monitor app-insights component create \
--app myapp-insights \
--resource-group myapp-rg \
--location eastus
# Obter a chave de instrumentação
instrumentationKey=$(az monitor app-insights component show \
--app myapp-insights \
--resource-group myapp-rg \
--query instrumentationKey -o tsv)
# Definir na aplicação web
az webapp config appsettings set \
--resource-group myapp-rg \
--name myapp-12345 \
--settings APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=$instrumentationKey"Adicione o pacote NuGet à sua aplicação:
dotnet add package Microsoft.ApplicationInsights.AspNetCore// Program.cs
builder.Services.AddApplicationInsightsTelemetry();Referência útil da CLI
# Visualizar os logs da aplicação em tempo real
az webapp log tail --resource-group myapp-rg --name myapp-12345
# Exibir as configurações atuais da aplicação
az webapp config appsettings list --resource-group myapp-rg --name myapp-12345
# Reiniciar a aplicação
az webapp restart --resource-group myapp-rg --name myapp-12345
# Obter a URL da aplicação
az webapp show --resource-group myapp-rg --name myapp-12345 --query defaultHostName -o tsv
# Listar todas as aplicações em execução em um grupo de recursos
az webapp list --resource-group myapp-rg --query "[].{Name:name, State:state, URL:defaultHostName}" -o tablePara Onde Ir a Partir Daqui
O App Service é o caminho de menor resistência para uma aplicação .NET que já compila e roda, porque a plataforma cuida do runtime para você. Se preferir controlar o runtime — para fixar uma versão específica do .NET, adicionar dependências nativas ou manter o deploy portátil entre provedores — empacote a aplicação você mesmo com um Dockerfile para .NET.
Se ainda não escolheu um provedor, a comparação de hosting .NET coloca o App Service ao lado de Railway, Fly.io e Render em níveis de plano e trade-offs.