Deploying Your First Azure App Service: Step-by-Step
This page walks through creating an Azure App Service app from scratch and deploying a working web application to it. You will create a resource group, App Service Plan, and web app, then deploy a Node.js app using ZIP deployment. Each step explains what you are creating and why — not just what command to run.
What you need
- Azure CLI installed and authenticated (
az login) - A web application to deploy — this tutorial uses a simple Node.js Express app, but the steps apply to any supported runtime
If you need to install the CLI, see installing the Azure CLI. For App Service capabilities in general, see Azure App Service overview.
Step 1: Create a sample application
Create a minimal Node.js Express app to deploy. If you already have an app, skip this step.
mkdir my-azure-app && cd my-azure-app
# Create package.json
cat > package.json << 'EOF'
{
"name": "my-azure-app",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.18.0"
}
}
EOF
# Create the application
cat > app.js << 'EOF'
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello from Azure App Service',
env: process.env.NODE_ENV || 'development',
timestamp: new Date().toISOString()
});
});
app.listen(port, () => {
console.log(`App running on port ${port}`);
});
EOF
npm installApp Service sets the PORT environment variable to tell your application which port to listen on. Always read the port from process.env.PORT (Node.js), os.environ.get(‘PORT’) (Python), or the equivalent for your language. Your app will fail to receive traffic if it hardcodes a port other than what App Service assigns.
Step 2: Create the Azure resources
# Create a resource group
az group create \
--name my-webapp-rg \
--location eastus
# Create an App Service Plan (Standard S1 — supports autoscaling and slots)
az appservice plan create \
--resource-group my-webapp-rg \
--name my-app-plan \
--sku S1 \
--is-linux
# Create the web app (Node.js 20 runtime)
az webapp create \
--resource-group my-webapp-rg \
--plan my-app-plan \
--name my-unique-webapp-$(date +%s) \
--runtime "NODE:20-lts"The —name must be globally unique across all of Azure — it becomes the subdomain of .azurewebsites.net. The $(date +%s) appends a timestamp to make it unique. Replace it with a meaningful name for real applications.
After creation, your app URL is https://<app-name>.azurewebsites.net. It currently shows a placeholder page because no code is deployed yet.
Step 3: Deploy your application
Package the application as a ZIP file and deploy it:
# From your app directory, create a ZIP excluding node_modules
zip -r app.zip . --exclude "node_modules/*" "*.git*"
# Deploy the ZIP to App Service
APP_NAME=$(az webapp list --resource-group my-webapp-rg --query "[0].name" --output tsv)
az webapp deployment source config-zip \
--resource-group my-webapp-rg \
--name $APP_NAME \
--src app.zipApp Service extracts the ZIP, detects the Node.js application (via package.json), runs npm install, and starts your app using the start script. The deployment typically completes in 30–60 seconds.
# Open the app in a browser
az webapp browse --resource-group my-webapp-rg --name $APP_NAMEYou should see the JSON response from your Express app. If the page shows an error, check the logs:
# Enable logging and stream real-time output
az webapp log config \
--resource-group my-webapp-rg \
--name $APP_NAME \
--application-logging filesystem \
--level information
az webapp log tail \
--resource-group my-webapp-rg \
--name $APP_NAMEStep 4: Configure environment variables
Set application settings that your app reads as environment variables:
az webapp config appsettings set \
--resource-group my-webapp-rg \
--name $APP_NAME \
--settings \
NODE_ENV=production \
API_VERSION=v2 \
LOG_LEVEL=infoAfter changing app settings, App Service restarts your app automatically to pick up the new values. You can verify they are set:
az webapp config appsettings list \
--resource-group my-webapp-rg \
--name $APP_NAME \
--output tableApp settings are encrypted in transit and at rest in Azure storage. However, they appear in plaintext in the portal. For sensitive values like database passwords, use Key Vault references — see Azure Key Vault overview.
Optional: Add a custom domain
The default domain (*.azurewebsites.net) works for testing, but production apps usually use a custom domain. App Service makes this straightforward:
# Add a custom domain to the web app
az webapp config hostname add \
--resource-group my-webapp-rg \
--webapp-name $APP_NAME \
--hostname www.yourdomain.comBefore running this command, add a CNAME record in your DNS provider pointing www.yourdomain.com to $APP_NAME.azurewebsites.net. App Service verifies domain ownership by checking this DNS record.
For HTTPS on a custom domain, create a free managed certificate:
az webapp config ssl create \
--resource-group my-webapp-rg \
--name $APP_NAME \
--hostname www.yourdomain.comAlternative: az webapp up for rapid prototyping
For quick experiments, az webapp up creates all necessary resources and deploys in a single command:
cd my-azure-app
az webapp up \
--name my-quick-app \
--runtime "NODE:20-lts" \
--sku F1 \
--location eastusThis creates a Free tier App Service Plan (F1) and a web app, then deploys from the current directory. Free tier has no autoscaling, no custom domain SSL, and shared infrastructure — good for learning, not for production. After prototyping, migrate to Standard or Premium tier for production workloads.
Cleaning up
# Delete all resources in the resource group
az group delete --name my-webapp-rg --yes --no-waitThis deletes the web app, App Service Plan, and all associated resources. If you want to keep the App Service Plan and just stop the web app from billing, you can stop the app instead:
az webapp stop --resource-group my-webapp-rg --name $APP_NAMEA stopped App Service app still incurs Plan charges (you pay for the underlying compute tier of the plan), but the app itself consumes no additional resources. To fully stop billing, you need to delete the App Service Plan or scale it to Free tier.
Common first-deployment mistakes
- Using a globally non-unique app name. App Service app names must be unique across all of Azure. If your name is taken, the CLI returns an error. Use a prefix that includes your organisation name or a timestamp.
- Including node_modules in the ZIP. Uploading node_modules inflates the ZIP size from kilobytes to hundreds of megabytes. App Service runs
npm installduring deployment — exclude node_modules from the ZIP with—exclude “node_modules/*”. - Not reading PORT from environment variables. App Service assigns a dynamic port via the
PORTenvironment variable. An app that listens on port 3000 unconditionally will fail to receive traffic because App Service routes to the port it told the app to use, not 3000.
Summary
- Three resources to create: a resource group, an App Service Plan (the compute tier), and the web app.
- ZIP deployment is the simplest deployment method. Exclude node_modules, vendor directories, or build caches from the ZIP.
- App settings set as key-value pairs become environment variables in your running app. Use Key Vault references for sensitive values.
- Always read the port from the
PORTenvironment variable — App Service assigns it dynamically. - For production, use Standard S1 tier minimum to get autoscaling and deployment slots.
Frequently asked questions
Can I deploy a Python or Node.js app to App Service?
Yes. App Service on Linux supports Python, Node.js, Java, .NET, PHP, and Ruby natively. Specify the runtime version in the app settings. For Python, the recommended approach is to include a requirements.txt file — App Service detects it and runs pip install automatically during deployment. For Node.js, include a package.json and App Service runs npm install.
What is the difference between az webapp up and deploying manually?
az webapp up is an all-in-one command that creates a resource group, App Service Plan, and web app if they do not exist, then deploys your code. It is designed for rapid prototyping. For production, you typically create these resources separately with explicit configuration, then use az webapp deployment source or a CI/CD pipeline for deployments.
How do I see my app logs in real time?
Enable application logging first: az webapp log config --application-logging filesystem. Then stream logs: az webapp log tail. You see stdout and stderr from your application in real time. Logs are also available in the Azure portal under the Monitoring section of your web app.