To host a Telegraf 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 (from US$1.99 a month), where the panel installs your packages, gives your code the token and starts the bot again if it crashes. In our test a Telegraf 4.16.3 bot used about 60 MB of memory on Node.js 22. Telegraf's bot.launch() uses long polling by default, which is what our plans need: they have no public HTTPS address for a webhook yet. The steps on PlayNHost:
- On Telegram bot hosting, click Create Server on a plan. In the order window, answer What is your bot for? with Telegram and pick Node.js.
- 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 Node.js Bot.
- At Connect to Telegram, paste the token from @BotFather under Paste your bot token, click Connect, check the bot name shown and click Yes, continue.
- At Your code, click Upload files and upload your
package.jsonandindex.jswithoutnode_modules, or click Connect Git. - On the Bot page, pane Code, check that Entry file is the file that starts your bot (
index.jsby default). Click Save if you change it. - Click Start bot and open Console. npm installs Telegraf on the first start, 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. This page is about Telegraf 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 | Install size |
|---|---|---|---|---|
| Telegraf | 4.16.3 | 60 MB | 0.2 s | 2.7 MB |
| grammY | 1.46.0 | 60 MB | 0.2 s | 3.0 MB |
| node-telegram-bot-api | 2.3.0 | 63 MB | 0.2 s | 2.4 MB |
| Node.js 22 alone, no library | 22.23.4 | 43 MB | none | none |
How: on our own node, in a throwaway container set up like a 1 GB bot plan (the Node.js 22 image and start command of our Node.js bot type, with Node's memory limit at 80 percent), each bot called Telegram with a fake token, which Telegram refused; memory is the resident memory 15 seconds after that answer, start time runs from launch to the answer, and each figure is the median of runs 2 to 4.
These are floors: a bot with no chats and no data yet. Your own code, what it keeps in memory and the number of chats add to them, so the library is a small part of the 0.5 GB STARTER plan.
A minimal Telegraf bot
It answers /start, reads the token the panel gives it and prints a ready line. Put both files in your bot's main folder.
package.json
{
"name": "my-telegraf-bot",
"private": true,
"main": "index.js",
"dependencies": {
"telegraf": "^4.16.3"
}
}
index.js
const { Telegraf } = require("telegraf");
const bot = new Telegraf(process.env.TELEGRAM_BOT_TOKEN);
bot.start((ctx) => ctx.reply("Hello! I am running on PlayNHost."));
// Without this, an error in one command stops the whole bot.
bot.catch((err, ctx) => {
console.error(`Error while handling update ${ctx.update.update_id}:`, err);
});
// launch() only returns when the bot stops, so the ready line goes in its callback.
bot.launch(() => console.log(`Logged in as @${bot.botInfo.username}`)).catch((err) => {
if (err.code === 401) {
console.log("Telegram did not accept the bot token. Paste the current one from @BotFather, then restart.");
} else {
console.error(err);
}
process.exit(1);
});
process.once("SIGINT", () => bot.stop("SIGINT"));
process.once("SIGTERM", () => bot.stop("SIGTERM"));
Telegraf's own examples read process.env.BOT_TOKEN. The panel gives your code the token as TELEGRAM_BOT_TOKEN and also as BOT_TOKEN, TOKEN, TELEGRAM_TOKEN and TELOXIDE_TOKEN, so those examples run without changes. The line Logged in as @your_bot is what turns the chip to Online in Telegram.
If the token is wrong
In our test, with a fake token, Telegraf's bot.launch() failed with a TelegramError whose err.code is 401. Printed as its name and message, it reads:
Error: 401: Unauthorized
The name shows as Error because Telegraf's error class does not set a name of its own. Telegraf does not retry a refused token: bot.launch() fails at once, and so does a bot that got no token at all (401: Bot Token is required). The bot above catches both and prints Telegram did not accept the bot token. Paste the current one from @BotFather, then restart. instead, and the panel 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 Telegraf on a server
- Releases: the newest Telegraf is 4.16.3, released on 29 Feb 2024. Version 4.16.0 (25 Feb 2024) added Telegram's Bot API 7.0 and 7.1; Telegram's own changelog is at Bot API 10.3 (24 Aug 2026). The GitHub repository was last pushed to on 24 Sep 2026. In our test 4.16.3 installed and started normally on Node.js 22.
- The ready line goes in the
launch()callback.bot.launch()returns only when the bot stops, so aconsole.logafterawait bot.launch()never prints while it runs. Since 4.16.0,launch()takes a callback that Telegraf calls once Telegram has accepted the token, withbot.botInfofilled in. - An error in a command stops the bot unless you set
bot.catch. Telegraf's default handler printsUnhandled error while processingwith the update and throws the error again, which ends polling, so the process stops. Our panel starts a crashed bot again within about a minute, but it is offline until then. Withbot.catch, as above, the error is printed and the bot carries on. - Each update has 90 seconds. An update whose handlers take longer is reported as an error (the
handlerTimeoutoption, 90,000 ms by default), which then goes tobot.catch. Passnew Telegraf(token, { handlerTimeout: ... })for longer jobs. - A webhook left on the bot is removed for you.
bot.launch()without awebhookoption callsdeleteWebhookbefore it polls, and keeps the messages waiting unless you passdropPendingUpdates: true.bot.launch({ webhook: ... })needs a public HTTPS address, which these plans do not include yet: use plainbot.launch(). Moving from a webhook host: How to move a Telegram bot to PlayNHost. - Another copy of the bot stops this one. On
409 Conflict(the same token polling somewhere else) Telegraf's polling throws instead of retrying. The panel shows Your bot is running somewhere else too: stop the copy on your PC or your old host. See Fix Telegram bot 409 Conflict. While it polls, network errors,429and Telegram's own5xxerrors are retried after the wait Telegram asks for, or 5 seconds. - Node.js version: Telegraf 4.16.3 asks for Node.js 12.20 or 14.13.1 and newer. The Runtime on the Code pane offers Node.js 20, 22 (recommended), 24, 25 and 26.
- Memory limit: the start script sets
NODE_OPTIONS=--max-old-space-sizeto 80 percent of your plan's memory, so the rest of the process has room. Details of the Node.js start script: How to host a Node.js Discord bot.
Host it yourself on a Linux VPS
On Debian 12 or 13, or Ubuntu 24.04, the distribution's package is enough for Telegraf (Debian 12 ships Node.js 18.20, Debian 13 ships 20.19, Ubuntu 24.04 ships 18.19):
sudo apt update
sudo apt install nodejs npm
node --version
Make a user for the bot, copy your files and install the packages:
sudo useradd --system --create-home --home-dir /opt/mybot mybot
sudo cp package.json index.js /opt/mybot/
sudo chown -R mybot: /opt/mybot
cd /opt/mybot && sudo -u mybot npm install --omit=dev
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=Telegraf Telegram bot
Wants=network-online.target
After=network-online.target
[Service]
User=mybot
WorkingDirectory=/opt/mybot
EnvironmentFile=/etc/mybot.env
ExecStart=/usr/bin/node index.js
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 after a crash or a process.exit(1). systemctl stop mybot sends SIGTERM, which the process.once lines turn into bot.stop(). Stop any other copy first, or Telegram answers both with 409 Conflict.
Self-hosting or a bot plan
On a VPS you manage yourself:
- Operating system updates and security, and Node.js upgrades.
- Running
npm installafter each change, the service file, the token file and its permissions. - Reading
journalctlto find out why the bot stopped. - Backups of the bot's data.
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:
- A Node.js version picker, and
npm installwhenpackage.json, the lock file or the Node.js version change. - The token, checked with Telegram before it is saved, stored encrypted and given to your code.
- Starting the bot again after a crash. If it crashes again within a minute, it stays stopped and Console says why.
- Banners above Console that name the cause when Telegram refuses the token, another copy is running or a webhook is set, with a Remove webhook button on the Telegram pane.
- Upload, Git deploys and backups from the panel.
Questions people ask
Why does nothing print after await bot.launch()?
Because bot.launch() waits for polling to end, and polling runs as long as the bot does. Code after it runs only when the bot stops. Put start-up messages in the callback, bot.launch(() => ...), and register commands before you call launch().
Can I keep using Telegraf, or should I move to grammY?
A working Telegraf 4 bot runs on our plans as it is: there is nothing to change to host it. Its last release is from February 2024, so methods Telegram added after Bot API 7.1 have no Telegraf shortcut. grammY 1.46.0 (26 Aug 2026) supports Bot API 10.3, and our starter bot for Node.js uses it. Move when you need something newer than Telegraf offers.
My Telegraf bot stopped with 409: Conflict. What now?
Another copy is polling with the same token, often on your PC or your old host. Telegraf stops instead of retrying. The panel starts it again within about a minute; if it stops again within a minute of that, it stays stopped. Find and stop the other copy, or make a new token in @BotFather and paste it with Replace token.
Can I run Telegraf on Bun?
Yes. When you order, pick Bun instead of Node.js: our Telegram bot hosting page lists Telegraf for it. The steps on this page are for Node.js.
Ready to run your Telegraf bot around the clock? See the plans on Telegram bot hosting.



