In this tutorial, I’ll use a simple Python app to demonstrate four different Deployment/Build approaches. Each method has its own use cases and limitations. You can even combine them, for example, using your laptop as the deployment tool while still using Oryx as the build engine. The same concepts apply to other runtimes such as Node.js, PHP, and beyond.
TOC
- Introduction
- Deployment Sources
- From Laptop
- From CI/CD tools
- Build Source
- From Oryx Build
- From Runtime
- From Deployment Sources
- Walkthrough
- Laptop + Oryx
- Laptop + Runtime
- Laptop
- CI/CD concept
- Conclusion
1. Introduction
Deployment on Azure Linux Web Apps can be done through several different methods. When a deployment issue occurs, the first step is usually to identify which method was used. The core of these methods revolves around the concept of Build, the process of preparing and loading the third-party dependencies required to run an application. For example, a Python app defines its build process as pip install packages, a Node.js app uses npm install modules, and PHP or Java apps rely on libraries.
In this tutorial, I’ll use a simple Python app to demonstrate four different Deployment/Build approaches. Each method has its own use cases and limitations. You can even combine them, for example, using your laptop as the deployment tool while still using Oryx as the build engine. The same concepts apply to other runtimes such as Node.js, PHP, and beyond.
2. Deployment Sources
From Laptop
Scenarios:
- Setting up a proof of concept
- Developing in a local environment
Advantages:
- Fast development cycle
- Minimal configuration required
Limitations:
- Difficult for the local test environment to interact with cloud resources
- OS differences between local and cloud environments may cause integration issues
From CI/CD tools
Scenarios:
- Projects with established development and deployment workflows
- Codebases requiring version control and automation
Advantages:
- Developers can focus purely on coding
- Automatic deployment upon branch commits
Limitations:
- Build and runtime environments may still differ slightly at the OS level
3. Build Source
From Oryx Build
Scenarios:
- Offloading resource-intensive build tasks from your local or CI/CD environment directly to the Azure Web App platform, reducing local computing overhead.
Advantages:
- Minimal extra configuration
- Multi-language support
Limitations:
- Build performance is limited by the App Service SKU and may face performance bottlenecks
- The build environment may differ from the runtime environment, so apps sensitive to minor package versions should take caution
From Runtime
Scenarios:
- When you want the benefits and pricing of a PaaS solution but need control similar to an IaaS setup
Advantages:
- Build occurs in the runtime environment itself
- Allows greater flexibility for low-level system operations
Limitations:
- Certain system-level settings (e.g., NTP time sync) remain inaccessible
From Deployment Sources
Scenarios:
- Pre-package all dependencies and deploy them together, eliminating the need for a separate build step.
Advantages:
- Supports proprietary or closed-source company packages
Limitations:
- Incompatibility may arise if the development and runtime environments differ significantly in OS or package support
Type | Method | Scenario | Advantage | Limitation |
---|---|---|---|---|
Deployment | From Laptop | POC / Dev | Fast setup | Poor cloud link |
Deployment | From CI/CD | Auto pipeline | Focus on code | OS mismatch |
Build | From Oryx | Platform build | Simple, multi-lang | Performance cap |
Build | From Runtime | High control | Flexible ops | Limited access |
Build | From Deployment | Pre-built deploy | Use private pkg | Env mismatch |
4. Walkthrough
Laptop + Oryx
Add Environment Variables
- SCM_DO_BUILD_DURING_DEPLOYMENT=false
(Purpose: prevents the deployment environment from packaging during publish; this must also be set in the deployment environment itself.) - WEBSITE_RUN_FROM_PACKAGE=false
(Purpose: tells Azure Web App not to run the app from a prepackaged file.) - ENABLE_ORYX_BUILD=true
(Purpose: allows the Azure Web App platform to handle the build process automatically after a deployment event.)
Add startup command
bash /home/site/wwwroot/run.sh
(The run.sh file corresponds to the script in your project code.)
Check sample code
requirements.txt — defines Python packages (similar to package.json in Node.js).
Flask==3.0.3
gunicorn==23.0.0
app.py — main Python application code.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Deploy from Laptop + Oryx"
if __name__ == "__main__":
import os
app.run(host="0.0.0.0", port=8000)
run.sh — script used to start the application.
#!/bin/bash
gunicorn --bind=0.0.0.0:8000 app:app
.deployment — VS Code deployment configuration file.
[config]
SCM_DO_BUILD_DURING_DEPLOYMENT=false
Deployment
Once both the deployment and build processes complete successfully, you should see the expected result.
Laptop + Runtime
Add Environment Variables
(Screenshots omitted since the process is similar to previous steps)
- SCM_DO_BUILD_DURING_DEPLOYMENT=false
Purpose: Prevents the deployment environment from packaging during the publishing process. This setting must also be added in the deployment environment itself. - WEBSITE_RUN_FROM_PACKAGE=false
Purpose: Instructs Azure Web App not to run the application from a prepackaged file. - ENABLE_ORYX_BUILD=false
Purpose: Ensures that Azure Web App does not perform any build after deployment; all build tasks will instead be handled during the startup script execution.
Add Startup Command
(Screenshots omitted since the process is similar to previous steps)
bash /home/site/wwwroot/run.sh
(The run.sh file corresponds to the script of the same name in your project code.)
Check Sample Code
(Screenshots omitted since the process is similar to previous steps)
requirements.txt: Defines Python packages (similar to package.json in Node.js).
Flask==3.0.3
gunicorn==23.0.0
app.py: The main Python application code.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Deploy from Laptop + Runtime"
if __name__ == "__main__":
import os
app.run(host="0.0.0.0", port=8000)
run.sh: Startup script. In addition to launching the app, it also creates a virtual environment and installs dependencies, all build-related tasks happen here.
#!/bin/bash
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
gunicorn --bind=0.0.0.0:8000 app:app
.deployment: VS Code deployment configuration file.
[config]
SCM_DO_BUILD_DURING_DEPLOYMENT=false
Deployment
(Screenshots omitted since the process is similar to previous steps)
Once both deployment and build are completed, you should see the expected output.
Laptop
Add Environment Variables
(Screenshots omitted as the process is similar to previous steps)
- SCM_DO_BUILD_DURING_DEPLOYMENT=false
Purpose: Prevents the deployment environment from packaging during publish. This must also be set in the deployment environment itself. - WEBSITE_RUN_FROM_PACKAGE=false
Purpose: Instructs Azure Web App not to run the app from a prepackaged file. - ENABLE_ORYX_BUILD=false
Purpose: Prevents Azure Web App from building after deployment. All build tasks will instead execute during the startup script.
Add Startup Command
(Screenshots omitted as the process is similar to previous steps)
bash /home/site/wwwroot/run.sh
(The run.sh corresponds to the same-named file in your project code.)
Check Sample Code
(Screenshots omitted as the process is similar to previous steps)
requirements.txt: Defines Python packages (like package.json in Node.js).
Flask==3.0.3
gunicorn==23.0.0
app.py: The main Python application.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Deploy from Laptop"
if __name__ == "__main__":
import os
app.run(host="0.0.0.0", port=8000)
run.sh: The startup script. In addition to launching the app, it activates an existing virtual environment. The creation of that environment and installation of dependencies will occur in the next section.
#!/bin/bash
source venv/bin/activate
gunicorn --bind=0.0.0.0:8000 app:app
.deployment: VS Code deployment configuration file.
[config]
SCM_DO_BUILD_DURING_DEPLOYMENT=false
Deployment
Before deployment, you must perform a local build process. Run commands locally (depending on the language, usually for installing dependencies).
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
After completing the local build, deploy your app.
Once deployment finishes, you should see the expected result.
CI/CD concept
For example, when using Azure DevOps (ADO) as your CI/CD tool, its behavior conceptually mirrors deploying directly from a laptop, but with enhanced automation, governance, and reproducibility. Essentially, ADO pipelines translate your manual local deployment steps into codified, repeatable workflows defined in a YAML pipeline file, executed by Microsoft-hosted or self-hosted agents.
A typical azure-pipelines.yml defines the stages (e.g., build, deploy) and their corresponding jobs and steps.
Each stage runs on a specified VM image (e.g., ubuntu-latest) and executes commands, the same npm install, pip install which you would normally run on your laptop.
The ADO pipeline acts as your automated laptop, every build command, environment variable, and deployment step you’d normally execute locally is just formalized in YAML. Whether you build inline, use Oryx, or deploy pre-built artifacts, the underlying concept remains identical: compile, package, and deliver code to Azure. The distinction lies in who performs it.
5. Conclusion
Different deployment and build methods lead to different debugging and troubleshooting approaches. Therefore, understanding the selected deployment method and its corresponding troubleshooting process is an essential skill for every developer and DevOps engineer.