⏱️ Lectura: 11 min

Telegram just turned its own infrastructure into your bot’s hosting: with Telegram Serverless, the code that responds to a message or a button runs directly on Telegram’s servers, without you having to spin up a VPS, a container, or a third-party cloud function.

📑 En este artículo
  1. TL;DR
  2. What Telegram Serverless is and why it matters
  3. How the three-layer mental model works
  4. Practical examples: from hello world to a real use case
  5. How to get started step by step
  6. Real-world use cases
  7. Common mistakes and best practices
  8. Comparison with alternatives
  9. Going deeper: what happens under the hood
  10. Frequently Asked Questions
    1. Does Telegram Serverless completely replace a VPS?
    2. What happens to the data if my bot suddenly gets a lot of traffic?
    3. Can I use external npm libraries inside the modules?
    4. Do I need a Telegram Business or special developer account?
    5. How do I test a handler before deploying it to production?
    6. Can schema.js migrations be reverted?
  11. References
    1. 📚 Artículos relacionados

If you’ve ever set up a server just so your bot could reply to /start, this platform eliminates that step entirely: you write JavaScript modules, deploy them with a single command, and Telegram runs them alongside its own API and a built-in database.

TL;DR

  • Understand what Telegram Serverless is and how it replaces a VPS or cloud function for bots.
  • Learn the three-layer model: local folder, Telegram’s cloud, and the tgcloud CLI.
  • Write a handlers/message.js file that responds to updates using the API and a SQLite database.
  • Know how to define tables with schema.js using the SDK’s table() and integer() DSL.
  • Deploy with npx tgcloud push and migrate the schema with npx tgcloud migrate.
  • Know when Serverless makes sense versus a traditional VPS or an external cloud function.
  • Identify common mistakes when migrating an existing bot to this model.

What Telegram Serverless is and why it matters

Telegram Serverless is a backend runtime built into the platform: it lets you write the code that processes your bot’s updates (messages, buttons, inline queries) as plain JavaScript modules, without managing any server of your own. Telegram runs it in an isolated V8 sandbox that runs close to the Bot API, which reduces the latency between receiving an update and responding to it.

This approach solves a problem anyone who’s built a bot knows well: keeping /start working 24/7 required an always-on process, reachable from the internet and kept patched. With Serverless that process doesn’t exist: your code runs on demand and scales automatically with the bot’s traffic.

The platform isn’t a template for a single type of app. According to the official documentation, it works for conversational bots with per-user state, for a Mini App’s backend, for games with leaderboards, and for automations that call external HTTP APIs and post results to a chat.

📌 Note: before writing a single line of code you need to enable the feature. In @BotFather: open your bot, go to Serverless, and turn it on. That unlocks the CLI token, the handlers, the library, and the database.

How the three-layer mental model works

You work across three places that map one-to-one. Your local folder holds the version-controlled code; Telegram’s cloud holds the deployed copy of those modules plus your bot’s database; and the tgcloud CLI is the bridge that shows you the differences between the two and syncs them.

Developer writing bot code on a laptop
The code lives in your local folder, version-controlled like any other project. Photo by Chris Boyer on Unsplash

You never SSH into anything. You edit files locally, run npx tgcloud push, and the platform handles the rest. Your bot’s traffic is handled by the deployed copy; the database persists across invocations even though each handler execution is ephemeral.

A project has only three kinds of code: handlers/ with one file per update type, lib/ with shared code you import from any handler, and schema.js with your database tables.

flowchart TD
    A["Local folder: handlers, lib, schema.js"] --> B["npx tgcloud push"]
    B --> C["Telegram's cloud: modules + database"]
    C --> D["Bot responds to updates in production"]

When an update arrives (a message, a button press, an inline query), Telegram routes it to the corresponding handler (handlers/message.js, handlers/callback_query.js, etc.) and calls its default export. That function talks to the Bot API and to the database through the SDK, and returns. An update with no handler to catch it is simply ignored, so you only add the handlers you need.

Practical examples: from hello world to a real use case

The simplest possible handler receives a message and replies. It doesn’t touch the database, it imports nothing but the API SDK:

import { api } from 'sdk';

export default async function (message) {
  await api.sendMessage({
    chat_id: message.chat.id,
    text: `Received your message: ${message.text ?? '(no text)'}`,
  });
}

This file goes in handlers/message.js. Deployed with npx tgcloud push, it’s already a live bot that echoes every message: there’s no database or state, so there’s no need to run any migration.

The next example gets closer to a real use case: a support bot that counts how many tickets each chat has opened and remembers the last subject. First we define the table in schema.js:

import { table, integer, text } from 'sdk/db';

export const tickets = table('tickets', {
  chatId: integer('chat_id').primaryKey(),
  abiertos: integer('abiertos').notNull().default(0),
  ultimoAsunto: text('ultimo_asunto'),
});

And then the handler that inserts or updates that row on every message, using onConflictDoUpdate to increment the counter atomically:

import { api, db } from 'sdk';
import { tickets } from 'schema';
import { sql } from 'sdk/db';

export default async function (message) {
  const chatId = message.chat.id;
  const asunto = message.text ?? 'No subject';

  const [fila] = await db.insert(tickets)
    .values({ chatId, abiertos: 1, ultimoAsunto: asunto })
    .onConflictDoUpdate({
      target: tickets.chatId,
      set: { abiertos: sql`${tickets.abiertos} + 1`, ultimoAsunto: asunto },
    })
    .returning()
    .run();

  await api.sendMessage({
    chat_id: chatId,
    text: `Ticket #${fila.abiertos} registered for this chat. Subject: ${fila.ultimoAsunto}`,
  });
}

With this you already have a bot with persistent per-chat state, without having touched a server. The expected result: every new message returns an incrementing ticket number and stores the last subject received.

sequenceDiagram
    participant U as User
    participant T as Telegram
    participant H as Handler in V8
    participant D as Database
    U->>T: sends message
    T->>H: delivers the update
    H->>D: inserts or updates row
    D-->>H: returns updated row
    H-->>T: calls api.sendMessage
    T-->>U: shows the response

How to get started step by step

You’ll need Node.js 18 or higher and a bot already registered with @BotFather. First, install Node according to your operating system.

Windows:

winget install OpenJS.NodeJS.LTS

macOS:

brew install node

Linux (Debian/Ubuntu):

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

With Node installed, the rest of the steps are identical across all three platforms. First enable Serverless in @BotFather (Bot → Serverless → enable) and then create the project:

npm create @tgcloud/bot support_bot
cd support_bot

The command scaffolds a folder ready to edit, with a sample handler at handlers/message.js and a docs/tgcloud-sdk.md file with the SDK reference. Pass . as an argument if you want to scaffold into the current folder.

Write your schema.js and your handlers as in the previous examples, and then deploy:

npx tgcloud push
npx tgcloud migrate
npx tgcloud status

push uploads your modules, migrate applies pending schema changes against the cloud database, and status shows you which version is running in production and whether there are undeployed differences. Use run to test a specific handler before deploying.

Real-world use cases

An AI conversational bot that needs to remember each user’s context across messages fits naturally: the state lives in the table, not in the memory of a process that could restart. A Mini App backend can use the same handlers to serve dynamic data to the frontend embedded in Telegram without exposing a separate public endpoint.

For games and tools, the built-in database works for leaderboards and quizzes without contracting an external engine. And for automations and integrations, the outbound HTTP available in every module lets you call third-party APIs and post the result to a channel, for example a bot that checks a feed periodically and posts alerts.

Common mistakes and best practices

The most common mistake when migrating an existing bot is forgetting that there’s no persistent filesystem: any state your bot needs to remember between invocations has to live in the schema.js table, not in a global variable or a temporary file.

⚠️ Watch out: if you run tgcloud push without having run tgcloud migrate after adding a new table, the handler will fail when it tries to read or write to a table that doesn’t yet exist in the deployed database.

Another typical oversight is assuming that an update with no handler triggers some kind of error: it’s actually ignored silently, so if your bot doesn’t respond to a certain update type, check first whether the corresponding file is missing from handlers/ before suspecting the internal logic.

Finally, treat migrations as reviewed code, not an automatic step: every schema change gets recorded and version-controlled along with the rest of the project, so a git diff on schema.js should be as readable as any other code change.

Comparison with alternatives

Option When to use it Advantage Limitation
Telegram Serverless Bots and Mini Apps that don’t need their own infrastructure Zero servers, built-in database and API, atomic deploy Tied to Telegram, doesn’t work for logic outside the bot ecosystem
Your own VPS Bots that already integrate other in-house services or need full control of the environment Absolute control over the runtime and operating system Has to be kept running, patched, and scaled manually
Cloud Function (Lambda, Cloud Run) Backends that serve other clients or channels besides Telegram Portable across providers, integrates with the rest of a cloud stack You have to wire up the Bot API, the database, and the credentials yourself
Hosting panel (cPanel/Heroku-style) Quick prototypes with a graphical admin interface Visual configuration, little infrastructure knowledge needed Less fine-grained control, dependency on the panel provider

Going deeper: what happens under the hood

Each handler invocation runs in a lightweight V8 isolate, the same isolation mechanism used by runtimes like Cloudflare Workers or Deno Deploy: a separate execution context per invocation, without the cost of starting a full operating system process. Because it runs physically close to the Bot API infrastructure, calls to api.* and to the database have fewer network hops than a handler hosted on another cloud provider.

💭 Key point: the deploy is atomic: tgcloud push replaces all modules at once, not file by file, so there’s no window where the bot runs with a mix of old and new code.

The included database is backed by SQLite, which explains why the SDK exposes a table DSL (table(), integer(), text()) instead of asking you for a connection string: there’s no separate database server to configure, just the file managed by the platform.

flowchart LR
    A["Edit schema.js and handlers/"] --> B["npx tgcloud push"]
    B --> C["npx tgcloud migrate"]
    C --> D["npx tgcloud status"]
    D --> E["Bot in production"]

To confirm a deploy went live, npx tgcloud status shows the deployed version and whether your local copy differs from the cloud; no external panel is needed to check it.

📖 Summary on Telegram: View summary

Your next step: clone the sample project with npm create @tgcloud/bot, add your own table in schema.js, and deploy it with tgcloud push followed by tgcloud migrate to see the full cycle on a real bot.

Frequently Asked Questions

Does Telegram Serverless completely replace a VPS?

For the bot’s own logic, yes. If your backend also serves other clients outside Telegram, you’ll still need that separate infrastructure.

What happens to the data if my bot suddenly gets a lot of traffic?

Execution scales automatically because each invocation runs in its own isolate; there’s no single process that gets overwhelmed.

Can I use external npm libraries inside the modules?

The project is structured as standard JavaScript code in lib/, although external dependencies are subject to what the platform’s V8 sandbox supports.

Do I need a Telegram Business or special developer account?

No, a bot registered with @BotFather with the Serverless option enabled from the bot’s panel is enough.

How do I test a handler before deploying it to production?

The CLI includes the run command to execute a specific handler locally before uploading it with push.

Can schema.js migrations be reverted?

They’re managed as reviewed changes within the normal tgcloud migrate flow, version-controlled together with the rest of the project just like any other code file.

References

  • Telegram Serverless: official documentation with the full reference for the SDK, the CLI, and the project model.
  • Telegram Bot API: reference for the methods available on the SDK’s api object.
  • SQLite: the database engine backing the storage included in every project.
  • Node.js: the required runtime, version 18 or higher, to use the tgcloud CLI.

📱 Enjoy this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.

Featured image: Photo by Hazel Z on Unsplash


Andrés Morales

Developer and AI researcher. Writes about language models, frameworks, developer tooling, and open source releases. Covers ML papers, the tech startup ecosystem, and programming trends.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.