In July 2026, Telegram began inviting some of its contest participants into a private Serverless test. The pitch is straightforward: put your bot's code and database on Telegram's infrastructure. On October 6, Mini App hosting joined the package, so the interface can live there too.
I got access, and I already had two VPSs, one in Finland and one in Germany. That gave me a practical question: would I still want either server for a small Telegram app?
AI assisted with the prototype, benchmark tooling, editing and English adaptation. The measurements come from the test environments described below; the code and raw results are available for inspection.
What “serverless” actually changes
Your code still runs on a server. The difference is who looks after it. With a VPS, I prepare the runtime, start the application and arrange for it to recover if the process stops. With Telegram Serverless, I upload the code and Telegram runs it when an update or a Mini App call arrives.
For this project, three pieces were already there.
Somewhere to run the bot. I used npx tgcloud push to upload the handlers. I didn't create a persistent service or maintain an operating system for the bot.
A database. SQLite is built in. I defined the tables and applied a migration, then subsequent calls could read the same data.
Somewhere to host the interface. The Mini App got an HTTPS address alongside the bot. I didn't need another hosting account or domain. The platform verifies Telegram user data before my server-side endpoint receives it.
There is a trade-off. Server-side code uses JavaScript and Telegram's SDK. Ordinary npm imports and Node's filesystem APIs are unavailable there. The documented external HTTP response limit is 30 MB. The browser side can use npm packages; those server-side restrictions do not turn it into a restricted browser runtime.
A shared tree, built in one iteration
I vibe-coded the initial bot in one iteration (6.1 Sol): @commongroundlabbot. Send /start, choose “Открыть сад” (“Open garden”), then water the tree.
The Mini App inside Telegram on October 17. Opening the garden and watering were checked manually.
Everyone shares one tree. A watering contributes 120 growth units over two minutes. Each user gets one accepted watering per fixed minute-long time bucket. The bot, database and Mini App all run on Telegram's infrastructure.
The bot shows the same garden. The 14 gardeners in this first response are synthetic test participants, not an audience count.
The useful trick is that nothing needs to wake up every second. I save the watering time. When someone opens the garden, the handler calculates how much growth has accumulated.
How it works: save a watering, calculate growth when the garden is read, return the current state.
Twelve local checks and seven cloud checks passed. Replaying an event didn't create an extra watering, concurrent requests were counted, and a code update preserved the original 14 test records. Watering from both the chat and the Mini App worked too.
All right, let's compare
My first test covered three database operations: read the garden, accept a watering and replay an event. Both VPSs won. That told me something about this application's database path, but it was a thin basis for judging the whole platform.
So I added CPU work, JSON processing, batch inserts, a growing event log, concurrent SQL calls and outbound HTTP.
Each VPS has one vCPU and about 2 GB of RAM. Both ran the same Node.js and SQLite versions, with WAL and synchronous=FULL. I also caught a mistake in the first setup: Finland's /tmp was backed by RAM. I reran the final database tests on persistent ext4 storage on both VPSs. Telegram's SQLite was accessed through its SDK; I couldn't inspect its synchronization settings.
The table shows medians in milliseconds. There were three rounds, with warm-up samples excluded: 150 measurements for the key lookup, 30 for each 32-row insertion method, 90 for a garden read and 60 for a watering.
| Operation | Finland VPS | Germany VPS | Telegram Serverless |
|---|---|---|---|
| Read one row by primary key | 0.009 | 0.013 | 2 |
| Insert 32 rows separately | 18.16 | 95.48 | 533 |
| Insert 32 rows in one statement | 0.69 | 0.53 | 16 |
| Read the garden, 1,000 waterings | 0.26 | 0.25 | 7 |
| Accept a watering, 1,000 starting records | 0.87 | 0.74 | 22 |
For these SQL operations, both VPSs were faster. But look at the two insert rows. On Telegram, changing 32 separate calls into one statement took the median from 533 ms to 16 ms, roughly a 33-fold improvement without moving hosts. Batching helped both VPSs as well.
That difference isn't just an SDK overhead measurement. The number of statements, database calls and separate commits changes together.
The CPU test was much closer: two million integer-mixing iterations took about 2 ms in all three environments. The JSON test went the other way. Ten parse/stringify/parse cycles over a 530 KB document took 11.1 ms on Telegram, 29.8 ms in Finland and 34.2 ms in Germany.
For those tasks, I timed 30 blocks of repetitions and divided each block's duration by its repetition count. Telegram's internal clock returned zero for nontrivial CPU work, so I used the execution time reported by the run API. Those figures compare whole blocks, including their surrounding execution overhead. They aren't precision measurements of the underlying processors.
What happened when the garden got bigger?
At 10,000 waterings, reading the garden took roughly 2.1 ms on the VPSs and 36 ms on Telegram. At 100,000, the cloud query failed with rows_read limit exceeded.
A separate probe made the distinction clearer. Summing 50,000 rows succeeded; summing 50,001 failed. Yet a primary-key lookup in a table holding 100,001 rows still worked. The table could hold the data. The full scan was the problem.
I also checked writes. A single insert of 10,000 rows succeeded; 10,001 was rejected, with no partial insert left behind. Two 6,000-row inserts inside the same function succeeded. These were observed query limits on this early-access account, not published platform-wide guarantees.
For my garden, the next change would be to keep running totals instead of recalculating the entire history on every read. I haven't put that optimization into the live bot.
Concurrent increments at levels of 1, 4 and 8 preserved all 65 expected updates in each environment. Telegram used parallel SDK calls inside one function; the VPSs used separate SQLite connections in worker threads. That checks a small concurrency scenario, not the maximum number of users either platform can handle.
Finally, I fetched the same Telegram JavaScript file from each environment. Across 30 successful requests per environment, the medians were 16 ms from Serverless, 54 ms from Finland and 23 ms from Germany. Telegram won this particular outbound route. One URL is not an internet speed ranking.
SQL and compute timings exclude message delivery to the Telegram client and cold starts. I ran the shared model on the VPSs, not a complete HTTP deployment of the Mini App. The full methodology, p95 values, failures and raw measurements make those boundaries explicit.
My take after all the tests
After these tests, I'd choose a VPS for my working projects with a database. For this application, it gives me four advantages that matter:
- A faster database path. With 1,000 starting records, accepting a watering and returning the state took 0.74–0.87 ms instead of 22 ms, roughly 25–30 times less time.
- Freedom to build. I can choose the language, libraries and database. Telegram's server-side runtime limits me to JavaScript and its SDK, without ordinary npm packages.
- Control over the application. Processes, database settings and backups can be arranged around the project.
- The larger log passed. Both VPSs handled our query over 100,000 waterings. On Serverless, that same full-history calculation hit a query-read limit and would need a different design.
Serverless's strongest argument for me is the free start. The bot, database and Mini App incurred no hosting charge in this experiment, and I didn't have another server to maintain. That's our early-access experience; I found no public pricing in the documentation I checked.
For a prototype, I'd try Serverless again. For my working projects, the VPSs stay: database speed and freedom to build matter more to me.
How to try it
- Open @BotFather, choose Open, and select or create a test bot.
- Open Serverless. If access hasn't been granted, use Get access to Serverless to request it.
- After the invitation, enable Serverless and get the separate token from CLI Access, then Access token.
AI-generated walkthrough illustration, not a screenshot of BotFather. Labels and positions are illustrative.
With Node.js installed:
npm create @tgcloud/bot my_serverless_lab
cd my_serverless_lab
npx tgcloud login
npx tgcloud push
The CLI token is different from the Bot API token. login asks for it locally. After the upload, send the bot some text; the scaffold's starter handler echoes it back.
If you add tables in tgcloud/schema.js, apply them separately:
npx tgcloud migrate --dry-run
npx tgcloud migrate --safe
That was a useful lesson from this experiment: uploading code and creating tables are separate steps.
AI-generated illustration of the deployment steps.
The bot's source and instructions for running your own copy are in the GitHub repository.
Initial functional checks: October 10, 2026. Expanded benchmarks and documentation check: October 17, 2026.
If you'd like to follow the next experiment, you're welcome to join my Telegram channel, outside this site. I post in Russian.
Cover illustration generated with AI.





Top comments (0)