Hi everyone,
I’m currently working on setting up Appium in an Azure DevOps pipeline and I’d love to hear your advice or best practices.
Right now, I’m discussing what’s the “right” way to start Appium inside a pipeline step.
For example:
script: |
echo “Installing Appium…”
npm install -g appium
echo “Starting Appium…”
appium --log-level info 2>&1 | tee appium.log &
sleep 10
displayName: ‘Start Appium Server’
versus
script: |
echo “Installing Appium…”
npm install -g appium
echo “Starting Appium…”
nohup appium --log-level info 2>&1 | tee appium.log &
sleep 10
displayName: ‘Start Appium Server’
Both approaches seem to work, but I’m unsure which one is more reliable in the Azure DevOps hosted agent environment.
Do you have any recommendations or experiences with:
-
The best way to run Appium in Azure Pipelines?
-
Ensuring the Appium server stays alive during the test job?
-
Managing logs (console + log file)?
-
Running multiple Appium servers in parallel jobs?
-
How the options block and appium_server_url should be configured in a Python test script when running in the pipeline?
In my local environment this worked quite nicely:
capabilities = dict(platformName=‘Android’,automationName=‘uiautomator2’,deviceName=‘Android’,appPackage=‘com.android.settings’,appActivity=‘.Settings’,language=‘en’,locale=‘US’)
appium_server_url = ‘http://localhost:4723’
Any advice, lessons learned, or gotchas would be super helpful.
Thanks a lot!
Best,
Alex
Both approaches are suboptimal. There should be no hardcoded sleep after starting the server. Consider waiting until it replies to /status request with the given timeout:
HOST="localhost"
PORT="4723"
TIMEOUT="15"
URL="http://${HOST}:${PORT}/status"
INTERVAL=1 # seconds between retries
# Track time
START_TIME=$(date +%s)
echo "Checking service at $URL for up to $TIMEOUT seconds..."
while true; do
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
if [[ "$HTTP_STATUS" -eq 200 ]]; then
echo "Service is running on ${HOST}:${PORT} (HTTP 200)"
exit 0
fi
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - START_TIME))
if [[ "$ELAPSED" -ge "$TIMEOUT" ]]; then
echo "Timeout reached: Service is NOT running on ${HOST}:${PORT} (Last status: HTTP $HTTP_STATUS)"
exit 1
fi
sleep "$INTERVAL"
done
Thanks a lot!
Is nohup appium --log-level info > appium.log 2>&1 & a good approach for starting Appium in the background?
- script: |
nohup appium --log-level info > appium.log 2>&1 &
HOST="localhost"
PORT="4723"
TIMEOUT=15
URL="http://${HOST}:${PORT}/status"
INTERVAL=1
echo "Waiting for Appium to be ready at $URL ..."
START_TIME=$(date +%s)
while true; do
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
if [[ "$HTTP_STATUS" -eq 200 ]]; then
echo "✅ Appium is running on ${HOST}:${PORT}"
break
fi
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - START_TIME))
if [[ "$ELAPSED" -ge "$TIMEOUT" ]]; then
echo "❌ Timeout reached: Appium did not start on ${HOST}:${PORT}"
exit 1
fi
sleep "$INTERVAL"
done
displayName: "Start Appium"
without nohup the process would be killed as soon as the corresponding script step is completed
Thank you for your answer!
On Ubuntu and macOS, using nohup allows the process to keep running after the script step completes. How can I achieve the same behavior on Windows 10 and Windows 11 using PowerShell or CMD?
Github actions allow to set bash as run shell for Windows as well. If you interested about cmd in particular then this is what ChatGPT proposes:
Yes, there **is an alternative to the `nohup` command in Windows**, but since Windows and Unix-based systems (like Linux) handle processes differently, the equivalent behavior requires different tools or techniques.
---
## ✅ What `nohup` Does (Quick Reminder):
`nohup` (short for "no hang up") allows you to **run a command or script in the background**, even after the user logs out or the terminal is closed. The output is usually redirected to `nohup.out`.
---
## 🪟 Windows Alternatives to `nohup`
### 1. **`Start-Process` in PowerShell**
This is the most direct alternative. It allows launching a process **independently of the terminal**.
```powershell
Start-Process -FilePath "python" -ArgumentList "my_script.py" -WindowStyle Hidden
```
✅ *The process will continue running even after you close the PowerShell window.*
You can also redirect output:
```powershell
Start-Process -FilePath "python" -ArgumentList "my_script.py > output.log" -NoNewWindow
```
---
### 2. **`start` Command in CMD**
```cmd
start /b python my_script.py > output.log 2>&1
```
* `/b`: runs in background (without a new window)
* `> output.log 2>&1`: redirects both stdout and stderr to a file
⚠️ This method **may still terminate the process when the terminal is closed** — for truly detached execution, PowerShell or Task Scheduler is more reliable.
---
### 3. **Task Scheduler (GUI or `schtasks`)**
You can schedule a script to run **independently of any user session**:
```cmd
schtasks /Create /SC ONCE /TN "MyScript" /TR "C:\Path\To\python.exe C:\Path\To\my_script.py" /ST 12:00
```
Then start it with:
```cmd
schtasks /Run /TN "MyScript"
```
✅ Survives logout, system restarts, etc. Best for **long-running background tasks**.
---
### 4. **Using `pythonw.exe` for Python scripts (No Console Window)**
If you're running a Python script and want **no terminal window at all**:
```cmd
C:\Path\To\pythonw.exe C:\Path\To\my_script.py
```
* Works well for **GUI apps or background scripts**
* **No output or error shown**, so you need to handle logging inside your script
---
### Summary Table:
| Command | Detached? | Logging Support | Best Use |
| ---------------------------- | ------------- | ------------------------------ | -------------------------------- |
| `Start-Process` (PowerShell) | ✅ Yes | ✅ Yes | General background scripts |
| `start /b` (CMD) | ⚠️ Not always | ✅ Yes | Simple tasks, temporary |
| `schtasks` | ✅ Yes | ✅ Yes | Scheduled or persistent tasks |
| `pythonw.exe` | ✅ Yes | ❌ No (unless handled manually) | Silent background Python scripts |
---