Telegram bots are one of the most requested small projects on freelance platforms like Upwork and Fiverr, and building one yourself is far more approachable than it looks. With a basic grasp of JavaScript and an evening to spare, you can end up with a bot that replies to messages, handles commands, and shows custom keyboards.
The tricky part is usually not the code itself, it's what happens after the code works. A bot running on your laptop only works while that laptop is on and the terminal window stays open. Close the lid, and the bot goes silent. To keep a bot answering around the clock, it needs to live somewhere that never shuts down: a server.
This guide walks through the whole process in order. First we'll write a working bot in JavaScript and compare the libraries available for the job. Then we'll move the finished code to a VPS, set it up with PM2, and cover the mistakes that most commonly take a bot offline in its first week.
Why Your Bot Needs a Server, Not Just Your Laptop
It's entirely possible to build and test a Telegram bot on a personal computer, and for development that's exactly what you should do. The problem shows up the moment the bot needs to serve real users. If the process only runs while your machine is awake and connected, every restart, sleep cycle, or closed terminal takes the bot down with it.
A Telegram bot is also one of the lightest things you can host. It doesn't need a powerful machine: a single CPU core and a gigabyte or two of RAM is enough for most personal or small business bots. That makes a basic VPS instance the natural choice, cheap, always on, and independent of whatever is happening on your own computer.
This is exactly the kind of workload a low-cost VPS from Serverspace is built for: instances spin up in about a minute, and the smallest US data center plans are priced for side projects rather than enterprise infrastructure.
What You'll Need Before You Start
Four things, none of them expensive or hard to get.
- js. The JavaScript runtime the bot will run on. As of mid-2026, Node.js 24 is the current active LTS release and the right choice for a new project. Node.js 26 is newer but won't reach LTS status until October 2026, so it's better suited to experiments than to a bot you rely on.
- A code editor. VS Code is the common choice, but any editor with syntax highlighting works fine.
- A Telegram account. You'll use it to register the bot through Telegram's own bot, BotFather.
- A server for round-the-clock uptime. Covered in detail later in this guide.
Getting a Bot Token from BotFather
Before writing any code, the bot needs to be registered with Telegram itself in order to get a token, the credential that lets your code talk to the Bot API.
Open Telegram and search for @BotFather, the official bot used to create and manage other bots. Send the /newbot command and follow the prompts: first a display name that users will see, then a short technical username that must end in "bot".
BotFather then replies with a token, a long string of letters and numbers. This token is the key to the bot: anyone who has it can control the bot as if they were you.
Important: Never commit this token to a public GitHub repository or paste it directly into your source code. We'll cover the right way to store it in the next section.
Choosing a Library: Telegraf vs grammY vs node-telegram-bot-api
It's possible to talk to the Telegram Bot API directly over plain HTTP requests, but that means manually handling every request and parsing every event by hand. A library takes care of that boilerplate.
Three options come up most often for JavaScript bots.
- node-telegram-bot-api, the oldest of the three. It still shows up in a lot of tutorials because it was one of the first libraries available, but it depends on outdated packages and isn't actively maintained. It's fine for following along with a lesson, but not a great foundation for a new project in 2026.
- Telegraf, a battle-tested library with a large community. It comes with built-in scenes (for multi-step conversations) and sessions (for storing per-user state), which is useful once a bot's logic gets more complex.
- grammY, a newer library written in TypeScript. Over the past year it has overtaken Telegraf in weekly downloads and keeps gaining ground, largely because it favors a simple, predictable API and works well with typed code even if your own project stays in plain JavaScript.
Either of the two actively maintained options works fine for a small or medium bot. Here's a side-by-side comparison to help decide.
| Library | Popularity | Strengths | Best For |
|---|---|---|---|
| node-telegram-bot-api | Declining, updated infrequently | Simple API, but relies on deprecated dependencies | Learning exercises only, not recommended for new projects |
| Telegraf | Consistently high, large community | Built-in scenes and sessions, rich plugin ecosystem | Bots with complex, multi-step conversation logic |
| grammY | Growing fastest, has overtaken Telegraf in downloads | Written in TypeScript, lightweight and modern middleware approach | New projects and developers who want a current, actively developed stack |
This guide uses grammY for the code examples, since it's a solid default for a new project, but the same logic maps almost line for line onto Telegraf if that's the library you'd rather use.
Writing Your First Bot
Create a project folder and initialize it as a Node.js project:
mkdir my-telegram-bot
cd my-telegram-bot
npm init -y
Install the library along with dotenv, which will keep the bot token out of the source code:
npm install grammy dotenvCreate a .env file in the project root and add the token you got from BotFather:
BOT_TOKEN=your_botfather_tokenNow create the main file, index.js:
require('dotenv').config();
const { Bot } = require('grammy');
const bot = new Bot(process.env.BOT_TOKEN);
bot.command('start', (ctx) => {
ctx.reply('Hi! I am a simple JavaScript bot. Send me a message.');
});
bot.on('message:text', (ctx) => {
ctx.reply(`You said: ${ctx.message.text}`);
});
bot.start();
A quick walkthrough of what's happening. require('dotenv').config() loads the variables from .env, so the token is available as process.env.BOT_TOKEN instead of sitting in plain text in the file. The bot object is created with that token and becomes the entry point for all the logic. bot.command('start', ...) fires when a user sends /start, and bot.on('message:text', ...) reacts to any text message and echoes it back.
Run the bot locally:
node index.jsIf everything is set up correctly, the terminal won't print anything, but the bot will already be responding in Telegram. Open a chat with it and send /start, you should get the welcome message back. From here you can add button handling, inline keyboards, or a call to an external API, but this is already a working bot.
Webhooks vs Long Polling: Which One to Use
The code above runs the bot in long polling mode: it repeatedly asks Telegram's servers whether there are new messages. That's the simplest way to receive updates, and it's exactly what bot.start() does by default.
The alternative is a webhook. Instead of the bot asking Telegram, Telegram sends a POST request to a URL you provide whenever a new event happens.
- Long polling needs no domain, no SSL certificate, and no open inbound ports, just an outbound connection to the internet. That makes it convenient for development and for smaller personal bots. The tradeoff is latency: messages can occasionally arrive a few seconds late, and the constant checking adds a small amount of background load.
- Webhooks are faster: updates arrive instantly, with no idle polling at all. The catch is that a webhook needs its own domain with a valid HTTPS certificate, which adds setup work upfront.
For getting started, and for most personal or small production bots, long polling is a perfectly reasonable default. Webhooks start to make sense once a bot has a meaningful user base and a few seconds of delay actually matters, or when it's already running alongside other services on the same domain.
Deploying the Bot to a VPS
Spinning Up and Preparing the Server
Because a Telegram bot needs so little in the way of resources, almost any entry-level VPS plan will do. Even a bot with a few thousand active users typically runs comfortably on a single core and a gigabyte or two of RAM. It's only worth sizing up if the bot is also doing something heavier in the background, like image processing or frequent queries against a large database.
Any provider can host this, but Serverspace's US plans are worth a look here specifically because their entry-level tier is scoped for exactly this kind of lightweight workload, and the US data center keeps latency low if most of your users are stateside.
Once the server is ready, connect over SSH:
ssh root@your_server_ipUpdate the system first:
apt update && apt upgrade -yThen install the current Node.js LTS release. The NodeSource setup script is the quickest path:
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
apt install nodejs -y
Check the version:
node -vIf that returns something around v24.x.x, the server is ready for the next step.
Moving the Code and Setting Environment Variables
There are two straightforward ways to get the code onto the server. If the project already lives in a git repository, clone it directly:
git clone https://github.com/your_account/my-telegram-bot.git
cd my-telegram-bot
Without a repository, copy the files from your local machine with scp instead:
scp -r my-telegram-bot root@your_server_ip:/root/Either way, install the dependencies next:
npm installOne thing to watch for: the .env file usually isn't tracked by git (it belongs in .gitignore), so it needs to be recreated on the server:
nano .envPaste in the same BOT_TOKEN you used locally. Keeping the token in its own file, separate from the rest of the repository, is worth doing whether the repo is public or private, since it removes the risk of accidentally committing the token along with everything else.
Keeping the Bot Alive with PM2
Running node index.js and closing the terminal kills the process the moment the SSH session ends. Keeping it alive needs a process manager, and for Node.js that almost always means PM2.
Install it globally:
npm install -g pm2Start the bot through PM2:
pm2 start index.js --name telegram-botThe process now runs in the background, independent of the terminal. Check its status with:
pm2 statusLast step: make sure it comes back automatically after a server reboot:
pm2 save
pm2 startup
The second command prints an additional line that needs to be copied and run separately, usually a sudo command that hooks PM2 into systemd. After that, the bot will start itself whenever the VPS reboots, with no manual step required.
A few commands worth keeping handy:
- pm2 logs telegram-bot streams the logs in real time, useful when something isn't working as expected.
- pm2 restart telegram-bot restarts the process after a code update.
- pm2 stop telegram-bot stops the bot temporarily without removing it from PM2's process list.
Common Mistakes That Take Bots Offline
A handful of issues account for most of the problems new bot builders run into.
- Hardcoding the token instead of using .env. Paste the token into index.js and push that to a public repo, and it's exposed to anyone who looks. Keep it in .env only, and make sure .env is listed in .gitignore.
- Running polling and a webhook at the same time. If both are active on the server at once, the bot starts responding inconsistently or sending duplicate replies. Explicitly disable polling before switching to a webhook.
- Assuming the bot stays up on its own. The single most common reason a bot goes quiet a few hours after launch is that it was only ever running in an open terminal session. PM2 with pm2 startup, covered above, solves this.
- Running an outdated Node.js version. Some libraries drop support for older releases, and a few modern language features simply won't work. Stick to the current LTS branch and check on it occasionally.
- Leaving an unprotected port open after switching to webhooks. Once a bot moves to webhooks, the port needs to be locked down with a firewall so only Telegram's servers, or another trusted source, can reach it.
Where to Take It From Here
A bot that replies to commands and text messages is a solid starting point, not the finished product. From here, the usual next steps are:
- Add a database, even something as simple as SQLite, if the bot needs to remember users or store conversation history.
- Switch to a webhook once the user base grows large enough that long polling's latency becomes noticeable.
- Add inline keyboards and buttons for a smoother experience than asking users to type commands by hand.
- Run several bots on one server through PM2, each under its own process name, if the workloads are light enough to share a single VPS.
Conclusion
The path from an empty file to a bot running around the clock comes down to a handful of clear steps: get a token from BotFather, write the code with grammY or Telegraf, move it to a server, and run it through PM2 with autostart enabled.
From there, it's easier to grow the bot gradually, adding features as they're actually needed, rather than trying to plan for everything upfront.
A good way to start is to spin up a minimal VPS on Serverspace, deploy the bot from this guide, and see in practice which features it actually needs next.