What I have I have a Python bot that used to work on my VPS and I want to move it to Azure Web App Service. (To avoid confusion: I'm talking about the Azure Web App Service intended for websites, not Azure Bot Service. The reason is I want to keep my current code structure that is not adjusted to a specific platform, so I can move it back to my VPS or another provider at any point, and from what I read on Azure Bot Service, it sounds like a kind of vendor lock-in.) When I deploy my existing (original) bot to my Azure Web App it starts fine, but shuts down shortly. What is the problem Azure expects my code to serve WSGI-compatible HTTP requests. Because my original code is just a bot that runs in a loop and doesn't serve anything, after ~250 seconds Azure shuts down the app: Container my-bot didn't respond to HTTP pings on port: 8000, failing site start. See container logs for debugging I understand the solution is to use a framework such as Flask What I tried I tried to run Flask alongside my loop. But whichever runs first apparently hijacks the process, and blocks the other one, so it never runs. To fix that I tried all kinds of combinations of multiprocessing, asyncio and threading. Here's the latest version I ended up with: import threading from flask import Flask, jsonify import logging import sys import asyncio import datetime import os import telegram import time logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler(sys.stdout)]) class Bot: def __init__(self): self.bot_token = os.getenv('TOKEN') self.chat_id = os.getenv('CHAT_ID') self.telegram_bot = telegram.Bot(token=self.bot_token) self.sleep = os.getenv('SLEEP') # set to 60 def run(self): while True: current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') message = f"It's {current_time} o'clock!\n\nSent from " + __file__ self.send_notification(message) time.sleep(int(self.sleep)) def send_notification(self, message): async def send_message(): await self.telegram_bot.send_message(chat_id=self.chat_id, text=message) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(send_message()) bot = Bot() app = Flask(__name__) # Simple endpoint that responds to Azure ping @app.route('/') def home(): return jsonify( hello='world', now=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') ), 200 if __name__ == '__main__': bot_thread = threading.Thread(target=bot.run) bot_thread.daemon = True # ChatGPT says this ensures the bot thread will stop when the main thread exits bot_thread.start() app.run(host=os.getenv("HTTP_HOST", "0.0.0.0"), port=int(os.getenv("HTTP_PORT"))) This works fine on my local: it both 1. serves HTTP requests and 2. sends out a message every 60 seconds. Azure The above runs and serves HTTP requests, but the bot does not work. No errors in logs. Checked all logs under https://.scm.azurewebsites.net/api/logs/docker This is a Linux Web App on their free tier. { "type": "Microsoft.Web/sites", "kind": "app,linux", "properties": { "state": "Running", "usageState": "Normal", "siteScopedCertificatesEnabled": false, "afdEnabled": false, "siteProperties": { "properties": [ { "name": "LinuxFxVersion", "value": "PYTHON|3.13" }, { "name": "WindowsFxVersion", "value": null } ], "appSettings": null }, "availabilityState": "Normal", "csrs": [], "cers": null, "siteMode": null, "computeMode": null, "serverFarm": null, "reserved": true, "isXenon": false, "hyperV": false, "storageRecoveryDefaultState": "Running", "contentAvailabilityState": "Normal", "runtimeAvailabilityState": "Normal", "dnsConfiguration": {}, "vnetRouteAllEnabled": false, "containerAllocationSubnet": null, "useContainerLocalhostBindings": null, "vnetImagePullEnabled": false, "vnetContentShareEnabled": false, "outboundVnetRouting": null, "siteConfig": { "numberOfWorkers": 1, "defaultDocuments": null, "netFrameworkVersion": null, "phpVersion": null, "pythonVersion": null, "nodeVersion": null, "powerShellVersion": null, "linuxFxVersion": "PYTHON|3.13", "windowsFxVersion": null, "windowsConfiguredStacks": null, "requestTracingEnabled": null, "remoteDebuggingEnabled": null, "remoteDebuggingVersion": null, "httpLoggingEnabled": null, "azureMonitorLogCategories": null, "acrUseManagedIdentityCreds": false, "acrUserManagedIdentityID": null, "logsDirectorySizeLimit": null, "detailedErrorLoggingEnabled": null, "publishingUsername": null, "publishingPassword": null, "appSettings": null, "metadata": null, "connectionStrings": null, "machineKey": null, "handlerMappings": null, "documentRoot": null, "scmType": null, "use32BitWorkerProcess": null, "webSocketsEnabled": null, "alwaysOn": false, "javaVersion": null, "javaContainer": null, "javaContainerVersion": null, "appCommandLine": null, "managedPipelineMode": null, "virtualApplications": null, "winAuthAdminState": null, "winAuthTenantState": null, "customAppPoolIdentityAdminState": null, "customAppPoolIdentityTenantState": null, "runtimeADUser": null, "runtimeADUserPassword": null, "loadBalancing": null, "routingRules": null, "experiments": null, "limits": null, "autoHealEnabled": null, "autoHealRules": null, "tracingOptions": null, "vnetName": null, "vnetRouteAllEnabled": null, "vnetPrivatePortsCount": null, "publicNetworkAccess": null, "cors": null, "push": null, "apiDefinition": null, "apiManagementConfig": null, "autoSwapSlotName": null, "localMySqlEnabled": null, "managedServiceIdentityId": null, "xManagedServiceIdentityId": null, "keyVaultReferenceIdentity": null, "ipSecurityRestrictions": null, "ipSecurityRestrictionsDefaultAction": null, "scmIpSecurityRestrictions": null, "scmIpSecurityRestrictionsDefaultAction": null, "scmIpSecurityRestrictionsUseMain": null, "http20Enabled": true, "minTlsVersion": null, "minTlsCipherSuite": null, "scmMinTlsCipherSuite": null, "supportedTlsCipherSuites": null, "scmSupportedTlsCipherSuites": null, "scmMinTlsVersion": null, "ftpsState": null, "preWarmedInstanceCount": null, "functionAppScaleLimit": 0, "elasticWebAppScaleLimit": null, "healthCheckPath": null, "fileChangeAuditEnabled": null, "functionsRuntimeScaleMonitoringEnabled": null, "minimumElasticInstanceCount": 1, "azureStorageAccounts": null, "http20ProxyFlag": null, "sitePort": null, "antivirusScanEnabled": null, "storageType": null, "sitePrivateLinkHostEnabled": null, "clusteringEnabled": false }, "functionAppConfig": null, "daprConfig": null, "deploymentId": "<app>", "slotName": null, "trafficManagerHostNames": null, "sku": "Free", "scmSiteAlsoStopped": false, "targetSwapSlot": null, "hostingEnvironment": null, "hostingEnvironmentProfile": null, "clientAffinityEnabled": false, "clientAffinityProxyEnabled": false, "blockPathTraversal": false, "clientCertEnabled": false, "clientCertMode": "Required", "clientCertExclusionPaths": null, "hostNamesDisabled": false, "ipMode": "IPv4", "vnetBackupRestoreEnabled": false, "domainVerificationIdentifiers": null, "kind": "app,linux", "managedEnvironmentId": null, "workloadProfileName": null, "resourceConfig": null, "containerSize": 0, "dailyMemoryTimeQuota": 0, "suspendedTill": null, "siteDisabledReason": 0, "functionExecutionUnitsCache": null, "maxNumberOfWorkers": null, "cloningInfo": null, "hostingEnvironmentId": null, "slotSwapStatus": null, "httpsOnly": false, "endToEndEncryptionEnabled": false, "functionsRuntimeAdminIsolationEnabled": false, "redundancyMode": "None", "inProgressOperationId": null, "geoDistributions": null, "privateEndpointConnections": [], "publicNetworkAccess": "Enabled", "buildVersion": null, "targetBuildVersion": null, "migrationState": null, "eligibleLogCategories": "AppServiceAppLogs,AppServiceAuditLogs,AppServiceConsoleLogs,AppServiceHTTPLogs,AppServiceIPSecAuditLogs,AppServicePlatformLogs,ScanLogs,AppServiceAuthenticationLogs", "inFlightFeatures": [ "SiteContainers" ], "storageAccountRequired": false, "virtualNetworkSubnetId": null, "keyVaultReferenceIdentity": "SystemAssigned", "autoGeneratedDomainNameLabelScope": null, "defaultHostNameScope": "Global", "privateLinkIdentifiers": null, "sshEnabled": true }, "apiVersion": "2022-03-01" } Render.com In addition to Azure, I deployed the above as a "Web Service" on Render.com. It works fine, both serving HTTP and sending me a message every 60 seconds. How do I make the above work with Azure? Continue reading...