NEW 📢 Limited launch offer: up to 70% off your first month on every game!See plans

How to host a Telegraf bot 24/7

Telegram bot hostingUpdated Nov 6, 202611 min read

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:

  1. 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.
  2. 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.
  3. 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.
  4. At Your code, click Upload files and upload your package.json and index.js without node_modules, or click Connect Git.
  5. On the Bot page, pane Code, check that Entry file is the file that starts your bot (index.js by default). Click Save if you change it.
  6. Click Start bot and open Console. npm installs Telegraf on the first start, then your bot prints Logged in as @your_bot and the chip on Overview turns to Online in Telegram.
  7. 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

LibraryVersionMemory at restStart to Telegram's answerInstall size
Telegraf4.16.360 MB0.2 s2.7 MB
grammY1.46.060 MB0.2 s3.0 MB
node-telegram-bot-api2.3.063 MB0.2 s2.4 MB
Node.js 22 alone, no library22.23.443 MBnonenone

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 a console.log after await bot.launch() never prints while it runs. Since 4.16.0, launch() takes a callback that Telegraf calls once Telegram has accepted the token, with bot.botInfo filled in.
  • An error in a command stops the bot unless you set bot.catch. Telegraf's default handler prints Unhandled error while processing with 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. With bot.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 handlerTimeout option, 90,000 ms by default), which then goes to bot.catch. Pass new Telegraf(token, { handlerTimeout: ... }) for longer jobs.
  • A webhook left on the bot is removed for you. bot.launch() without a webhook option calls deleteWebhook before it polls, and keeps the messages waiting unless you pass dropPendingUpdates: true. bot.launch({ webhook: ... }) needs a public HTTPS address, which these plans do not include yet: use plain bot.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, 429 and Telegram's own 5xx errors 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-size to 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 install after each change, the service file, the token file and its permissions.
  • Reading journalctl to 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 install when package.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.

Already hosting your bot somewhere else? Move it to us

OUR LATEST BLOGS