To host a Telegram.Bot (.NET) bot 24/7 you run it on a machine that stays on: a Linux VPS with a systemd service that starts it again when it stops, or a bot plan on PlayNHost Telegram bot hosting (from US$1.99 a month), where you upload your C# project, paste the token from @BotFather and press Start. In our test a Telegram.Bot 22.10.3.3 bot used about 61 MB of memory on .NET 8. Telegram.Bot receives messages by long polling, which is what our plans need: they have no public HTTPS address for a webhook yet. The steps on PlayNHost:
- On the Telegram bot hosting page, click Create Server on a plan. In the order window, answer What is your bot for? with Telegram and pick C# (.NET).
- Open your server in the panel. The Set up your bot card on Overview opens at Connect to Telegram; its first step, What runs, should say This bot runs .NET Bot.
- At Connect to Telegram, paste the token under Paste your bot token, click Connect, check the bot name shown and click Yes, continue.
- At Your code, click Upload files and upload
Bot.csprojandProgram.csto the main folder, without thebinandobjfolders, or click Connect Git. - On the Bot page, pane Code, keep Runtime at .NET 8 (recommended) and leave Entry file empty.
- Click Start bot and open Console. The server builds your project, then your bot prints
Logged in as @your_botand the chip on Overview turns to Online in Telegram. - On the Bot page, pane Telegram, click Open in Telegram and send your bot
/start.
Creating the bot in @BotFather and the panel steps are explained in How to host a Telegram bot, and the .NET bot type in general (both upload ways, build errors) in How to host a C# (.NET) Discord bot. This page is about Telegram.Bot itself: what it uses on a server and what to know before you host it.
What we measured
| Library | Version | Memory at rest | Start to Telegram's answer, with the build | Program launch to Telegram's answer |
|---|---|---|---|---|
| Telegram.Bot | 22.10.3.3 | 61 MB | 1.5 s | 0.3 s |
| .NET 8 alone, no library | none | 24 MB | 1.3 s (build and start, no call) | none |
How: on our own node, in a throwaway container set up like a 1 GB bot plan (the .NET 8 image and the start command of our .NET bot type, which runs dotnet publish on every start), the bot called Telegram's getMe with a fake token, which Telegram refused; memory is the resident memory 15 seconds after that answer, the first time column runs from the start of the build to the answer, the second from the program's launch, and each figure is the median of runs 2 to 4. The very first build, which also downloads the package, took 5 seconds.
These are floors: no chats, no data and no polling yet. Your own code, what it keeps in memory and the number of chats add to them. To skip the build on every start, you can upload the files you published on your own computer instead of the project (see the C# guide linked above). Compare the numbers with the plans on Telegram bot hosting and choose by what your bot does.
A minimal Telegram.Bot bot
It answers /start, reads the token from TELEGRAM_BOT_TOKEN (the panel gives it to your code under that name and also as BOT_TOKEN, TOKEN, TELEGRAM_TOKEN and TELOXIDE_TOKEN) and prints a ready line. Two files in the main folder:
Bot.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Telegram.Bot" Version="22.10.3.3" />
</ItemGroup>
</Project>
Program.cs
using Telegram.Bot;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Types;
const string TokenRefused = "Telegram did not accept the bot token. Paste the current one from @BotFather on the Bot page, then restart.";
string? token = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN");
if (string.IsNullOrWhiteSpace(token))
{
Console.WriteLine("No TELEGRAM_BOT_TOKEN found. Paste your bot token on the Bot page, then restart.");
return 1;
}
var bot = new TelegramBotClient(token);
User me;
try
{
me = await bot.GetMe();
}
catch (ApiRequestException e) when (e.ErrorCode == 401)
{
Console.WriteLine(TokenRefused);
return 1;
}
// Long polling only: remove a webhook left on the bot. Messages that wait are kept.
await bot.DeleteWebhook();
// Without an OnError handler, polling errors never reach the console. Set it before OnMessage.
bot.OnError += async (exception, source) =>
{
if (exception is ApiRequestException { ErrorCode: 401 })
{
Console.WriteLine(TokenRefused);
Environment.Exit(1);
}
Console.WriteLine($"Polling error ({source}): {exception.Message}");
await Task.Delay(TimeSpan.FromSeconds(5));
};
// Setting OnMessage starts polling in the background.
bot.OnMessage += async (message, type) =>
{
if (message.Text?.StartsWith("/start") == true)
{
await bot.SendMessage(message.Chat, "Hello! I am running on PlayNHost.");
}
};
Console.WriteLine($"Logged in as @{me.Username}");
await Task.Delay(Timeout.Infinite);
return 0;
On every start the server runs dotnet publish on Bot.csproj and starts Bot.dll. The console says [PlayNHost] Building your bot (the first build downloads packages and can take a minute), then [PlayNHost] Starting your bot, then Logged in as @your_bot, the line that turns the chip to Online in Telegram. Stop any copy you run on your computer with the real token first, or the two copies get 409 Conflict errors from Telegram.
If the token is wrong
In our test, with a fake token, Telegram.Bot's GetMe() failed with this error:
ApiRequestException: Unauthorized
The bot above catches that refusal (error code 401), before polling and during it, and prints Telegram did not accept the bot token. Paste the current one from @BotFather on the Bot page, then restart. instead, then stops with exit code 1. The panel reads that line and shows the banner Telegram did not accept your bot token above Console. To fix it, in @BotFather send /mybots, choose your bot, then API Token, and copy the token again. On the Bot page, pane Telegram, click Replace token, paste it, click Save token, then Restart now.
What to know about Telegram.Bot on a server
- Versions: Telegram.Bot 22.10.3.3 came out on 9 Oct 2026. It targets .NET Standard 2.0 and .NET 6, and its README recommends .NET 8 or newer, so any Runtime the panel offers (.NET 6, 8, 9 or 10) builds it; .NET 8 (recommended) matches the
net8.0above. - Method names lost
Asyncin version 22. It isGetMe()andSendMessage()now; tutorials withGetMeAsyncorSendTextMessageAsyncwere written for older versions, and those names are not in 22.10.3.3, so their code fails to build. The build error is just above[PlayNHost] The build failed. Read the errors above, fix them and press Start.in Console. - Setting
OnMessagestarts polling. As soon as a handler is added toOnMessage(orOnUpdate), the client polls Telegram in the background, and it asks for every update type. Add theOnErrorhandler first, as above. - Without
OnError, errors are invisible. When a poll fails, Telegram.Bot passes the error toOnError; with no handler it writes it to .NET's trace output, which does not reach the console. A bot whose token was revoked, or that another copy pushes out with409 Conflict, then looks running and answers nothing. The handler above prints each error, and since Telegram's own text is in the message, the panel can show Your bot is running somewhere else too or A webhook is set on your bot. - Slow down after an error. The polling loop asks again right after a failed request; it waits only as long as your
OnErrorhandler takes. The 5 secondTask.Delayabove keeps a lasting error from turning into a stream of requests. Telegram's own429 Too Many Requestsanswers are retried for you when Telegram asks to wait 60 seconds or less: by default a request is tried up to 3 times in all. - A webhook is not removed for you. Polling with a webhook set fails with
409 Conflict:can't use getUpdates method while webhook is active. The bot above callsDeleteWebhook()before polling, which keeps the messages waiting; you can also click Remove webhook on the Telegram pane. A webhook of your own needs a public HTTPS address, which our plans do not include yet. Moving from a webhook host: How to move a Telegram bot to PlayNHost. - Do not wait with
Console.ReadLine(). The first bot in the library's guide keeps running until you press Enter. On a server nobody presses Enter, and under systemd the program's input is empty by default, soReadLine()returns at once and the bot ends. Wait withawait Task.Delay(Timeout.Infinite), as above. - Crashes: when the bot exits, as the bot above does on a refused token, the panel starts it again right away, building it again first. If it stops again within a minute of that, it stays stopped and Console says why. See Fix Telegram bot 409 Conflict for the two kinds of 409.
Host it yourself on a Linux VPS
On a VPS, build the bot on your own computer and run only the result on the server. Publish it with the .NET 8 SDK:
dotnet publish Bot.csproj -c Release -o publish
On Ubuntu 24.04 the .NET 8 runtime is in Ubuntu's own packages:
sudo apt update
sudo apt install dotnet-runtime-8.0
dotnet --list-runtimes
Make a user for the bot and copy the publish folder to the server (for example with scp -r), then into its folder:
sudo useradd --system --create-home --home-dir /opt/mybot mybot
sudo cp -r publish/. /opt/mybot/
sudo chown -R mybot: /opt/mybot
Keep the token out of your code, in a file only root can read:
sudo sh -c 'echo "TELEGRAM_BOT_TOKEN=123456789:your-token" > /etc/mybot.env'
sudo chmod 600 /etc/mybot.env
Create /etc/systemd/system/mybot.service:
[Unit]
Description=Telegram.Bot Telegram bot
Wants=network-online.target
After=network-online.target
[Service]
User=mybot
WorkingDirectory=/opt/mybot
EnvironmentFile=/etc/mybot.env
ExecStart=/usr/bin/dotnet /opt/mybot/Bot.dll
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Start it now and at every boot, then follow its output:
sudo systemctl daemon-reload
sudo systemctl enable --now mybot
sudo journalctl -u mybot -f
Restart=always starts the bot again 5 seconds after it exits or crashes, but not after you stop it with systemctl stop. To update, publish again, copy the new files over the old ones and run sudo systemctl restart mybot. On Debian, .NET is not in the distribution's own packages; Microsoft's install instructions cover it.
Self-hosting or a bot plan
On a VPS you manage yourself:
- Operating system updates and security, and .NET runtime upgrades.
- Publishing and copying each new build, the service file, the token file and its permissions.
- Reading
journalctlto find out why the bot stopped or stopped answering. - Backups of anything your bot stores.
In return a VPS runs anything, and you can give it a domain with HTTPS for a webhook.
On a PlayNHost bot plan the panel does this for you:
- .NET 6, 8, 9 or 10 from a picker, and the build: upload the project and press Start.
- The token, checked with Telegram before it is saved, stored encrypted and given to your code under five names.
- Starting the bot again after a crash. If it crashes again within a minute, it stays stopped and Console says why.
- A Remove webhook button on the Telegram pane, and banners above Console that name the cause when a line shows a refused token, another copy or a webhook.
- Upload, Git deploys and backups from the panel.
Questions people ask
My Telegram.Bot bot runs but never answers. Why?
Most often polling fails and nothing says so: without an OnError handler the errors go to .NET's trace output, not the console. Add the handler from the bot above and restart. The usual causes are another copy polling with the same token (409 Conflict), a webhook still set on the bot, or a token revoked in @BotFather.
Can I use Telegram.Bot with a webhook here?
Not on our plans yet: a webhook needs a public HTTPS address, which they do not include. Polling needs none, because the bot asks Telegram for new messages, and it is what OnMessage does.
Does the build on every start slow my bot down?
Only the start: in our test the start took 1.5 seconds with the build against 0.3 seconds for the program alone. Once running, the bot is the same. To skip the build, publish on your computer and upload the files from the publish folder instead of the project.
Which .NET version should I pick?
.NET 8, the recommended one, with net8.0 in your project as above. Telegram.Bot also builds on .NET 6, 9 and 10; set the same version in TargetFramework and in Runtime. A project for a newer .NET than the Runtime stops the build with NETSDK1045; one for an older .NET builds but does not start, because that .NET is not installed in the image.
Ready to run your Telegram.Bot bot around the clock? See the plans on Telegram bot hosting.



