How I Built a Self-Hosted AI Assistant on Azure

It’s been a while since I’ve posted anything technical here, but I felt this one was worth documenting. As you can imagine, I’ve been spending a lot of time lately poking around at various AI tools. At some point that exploration turned into a project — building my own self-hosted AI assistant that I actually own and control, rather than renting access to someone else’s.

The concept is simple enough. I wanted something running on my own infrastructure, accessible from my phone, always on, and not subject to whatever a third-party decides to change next month. After some research I landed on OpenClaw, an open-source personal agent runtime that connects to the AI model of your choice and delivers responses through messaging channels like Telegram. I spent a day standing it all up on Azure. Here’s how that went.

Why Azure?

I’d had my assistant running on a VPS previously, but after cancelling those contracts I wanted to consolidate everything under one cloud provider and just deal with one invoice. Azure made sense — I was already familiar with it, the CLI tooling is solid, and Azure AI Foundry gives you access to a surprisingly wide catalog of AI models, all billed through the same subscription.

I’ll note that the official Microsoft guide I found for this kind of setup recommends Azure Bastion Standard SKU for SSH access. That runs about $140 a month just for the managed SSH layer. For a personal single-user Telegram bot, I can assure you that is absolutely not necessary. I went with a Standard B1ms VM at around $15 a month with a Network Security Group rule locking SSH down to my home IP. Roughly $16–17 a month all-in once you factor in model inference. Much more reasonable.

Picking a Model: The Part That Took Longer Than Expected

Before I wrote a single line of Azure CLI, I spent a fair amount of time on model selection. OpenClaw is model-agnostic, which sounds great until you realize it means you actually have to make a decision.

Azure AI Foundry’s catalog is broader than I expected. You’ve got the obvious OpenAI models, but also Anthropic’s Claude family, Meta’s Llama 4, Mistral, DeepSeek, Kimi, Qwen, and Microsoft’s own Phi family. I went through a pretty thorough evaluation looking at a few things that matter specifically when you’re running an always-on personal agent:

Does it call tools reliably? Can it hold a real conversation across multiple turns without losing the thread? How deep is its general world knowledge? And how long does it take to respond? That last one matters more than you’d think on a Telegram bot. Waiting a minute for an answer to a simple question feels broken, regardless of how good the answer is.

I looked at Microsoft’s MAI-DS-R1, which is an interesting model — it’s DeepSeek’s R1 reasoning architecture post-trained by Microsoft to clean up some content issues with the original. Well-priced at $0.30/$1.20 per million tokens, but R1’s deep reasoning mode fires on every single response by design. You’re waiting through a chain-of-thought process even when you ask it something simple. Better suited for a specialized research tool than an everyday assistant.

I also spent some time with Kimi K2.5, which has genuinely impressive agentic architecture — it can coordinate up to 100 parallel sub-agents apparently. That’s more than I need for a personal assistant, and at $0.60/$3.00 per million tokens with 40 tokens per second output, it felt like overkill.

I landed on DeepSeek V3.2 via Azure AI Foundry. It won on the combination of things that matter — it was specifically trained for agentic workloads with over 85,000 agent tasks in its pipeline, it has a 128K context window, strong world knowledge at 685 billion parameters, and it’s fast and decisive when calling tools. The cost works out to about $0.70 a month at my usage scale. That made the decision pretty easy.

Standing Up the Infrastructure

The Azure CLI setup was fairly methodical. Everything lives in a single resource group, which keeps things clean:

  • A Standard B1ms VM running Ubuntu 24.04
  • A Virtual Network with a dedicated subnet
  • An NSG with a single inbound SSH rule
  • An Azure Key Vault storing critical keys and tokens
  • An Azure AI Foundry resource with DeepSeek V3.2 deployed as a serverless endpoint

I hit one problem I didn’t expect. Ubuntu 24.04 uses socket-based SSH activation by default, which means the SSH socket wasn’t actually listening when I first tried to connect. The service showed as inactive in systemd even though the VM looked fine otherwise. It took me a little while to figure out what was going on, but once I did the fix was straightforward — you can use Azure’s run-command feature to execute shell commands on a VM through the Azure agent, completely bypassing SSH:

az vm run-command invoke \
  -g "${RG}" -n "${VM_NAME}" \
  --command-id RunShellScript \
  --scripts "sudo systemctl enable ssh.socket && sudo systemctl start ssh.socket && sudo systemctl enable ssh --now"

That’s now a trick I keep handy. If you’re ever locked out of an Azure VM, run-command is your escape hatch.

Installing and Configuring OpenClaw

Once I was actually inside the VM, OpenClaw installation is one command:

curl -fsSL https://openclaw.ai/install.sh | bash

The onboarding wizard takes you through model provider selection and channel setup, but since I was doing a custom Azure AI Foundry integration, I skipped both and configured everything manually in ~/.openclaw/openclaw.json.

The Azure Foundry integration for non-OpenAI models requires a custom provider config. A few things are different from a standard OpenAI setup that tripped me up:

  • Azure uses an api-key HTTP header instead of the standard Authorization: Bearer header, so you need to set "authHeader": false and pass the key through headers explicitly
  • You need "compat": { "supportsStore": false } because Azure doesn’t support the store parameter that OpenClaw sends by default

The other headache I ran into repeatedly: OpenClaw’s doctor --repair command keeps resetting gateway.mode to unset, which prevents the gateway from starting. I had to set this three separate times before I figured out the right fix, which is a systemd drop-in override file that sets it at the environment variable level — immune to whatever doctor --repair wants to do:

[Service]
Environment="OPENCLAW_GATEWAY_MODE=local"
Environment="NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache"
Environment="OPENCLAW_NO_RESPAWN=1"

Once that was in place, the gateway stayed up reliably.

Connecting Telegram

Telegram is the right first channel for this kind of setup. No webhooks to configure, no servers to expose, long polling works out of the box. You create a bot through @BotFather, get the token, and add it to the config. Simple.

For a single-user personal assistant, I went with dmPolicy: "allowlist" and pre-authorized my numeric Telegram user ID directly rather than going through the pairing flow. If your bot is only ever going to talk to one person, just say so and skip the ceremony.

One thing worth knowing: OpenClaw’s Telegram configuration lives under channels.telegram in the config, not under plugins. I saw a lot of posts online that had it in the wrong place, which produces a confusing “plugin not found” error. Trust the official docs here.

First Contact (And an Unexpected Self-Repair)

After uploading the completed config and restarting the gateway, I sent the first message. My assistant responded. That was a good moment.

During the onboarding conversation, it set up six cron jobs on its own — a morning weather report, daily disk space alerts, resource usage reports, weekly backup reminders, weekly update checks, and a monthly security audit. I thought that was a pretty sensible set of choices for a self-maintaining agent.

About a day in, it went silent on Telegram. I pulled the logs and found it had been trying to deliver cron job reports to a group chat it had been briefly added to during setup and then removed from. The errors in the log were pretty clear:

telegram message failed: bot is not a member of the channel chat

Before I had a chance to diagnose and fix this myself, it had already handled it. It noticed the failing delivery targets in its own logs, updated all six cron jobs to deliver to my DM instead, and resumed normal operation. It then sent me a detailed message explaining exactly what it had done and why.

I’ll be honest — I did not expect that on day one. That’s the agentic capability that DeepSeek V3.2 brings. Not just answering questions, but watching its own state and fixing problems. For something that’s supposed to run without constant hand-holding, that’s exactly what you want to see.

Things I Would Do Differently

A few lessons from the experience, in case you’re thinking about doing something similar:

Skip Azure Bastion from the start. The official Microsoft guide recommends it, but $140/month is completely unnecessary for a personal project. An IP-locked NSG rule gets you the same security outcome for free.

Write your config locally and scp it to the VM. I wasted time trying to edit JSON in nano on the VM. Writing the file on your local machine and uploading it is cleaner and avoids the kind of formatting errors that produce cryptic JSON parse failures.

Get gateway.mode local into the systemd drop-in on day one. That single missing config value was responsible for most of the gateway startup failures I hit.

Set groupPolicy disabled immediately if you’re running a single-user bot. If it’s never meant to be in a group, say so in the config from the beginning.

Where Things Stand

My assistant is running cleanly on OpenClaw 2026.3.24 with DeepSeek V3.2 via Azure AI Foundry. It’s accessible over Telegram, running six scheduled cron jobs, and I have an SSH shortcut set up on my Linux desktop for quick access when I need it. All credentials are stored in Azure Key Vault. Total monthly cost is around $16–17 including the VM, inference, and Key Vault.

For an always-on personal AI assistant running on infrastructure I fully control, that feels like a pretty good deal. If you’ve been thinking about building something similar, the combination of OpenClaw, Azure AI Foundry, and DeepSeek V3.2 is a solid stack. There are more moving parts than a hosted service, but the afternoon you spend standing it up is worth it.

Leave a comment