Telegram Bot VPS Guide: Polling vs Webhook, systemd and Cost (2026)

Run a Telegram bot 24/7 on a VPS: when to use long polling or a webhook, the official webhook requirements, a hardened systemd service, Caddy for HTTPS, sizing and monthly cost.

Title card reading “Telegram bot on a VPS” for the HourlyVPS guide to running a Telegram bot under systemd with long polling or a webhook

To run a Telegram bot on a VPS, install it as a systemd service on a small Linux server and choose how it receives updates: long polling (getUpdates) needs no domain, certificate or open port, while a webhook (setWebhook) needs a public HTTPS URL on port 443, 80, 88 or 8443. A Telegram bot VPS for a single text bot can be as small as 1 GB of RAM: Telegram’s flood limits cap how fast a bot can send, so you size for what your handlers do, not for Telegram. This guide uses Ubuntu 24.04 LTS, python-telegram-bot 22.8 and the official Bot API documentation, all checked on October 3, 2026.

Key takeaways

  • Long polling (getUpdates) needs no domain, certificate or open port; a webhook (setWebhook) needs HTTPS on port 443, 80, 88 or 8443, over IPv4, with TLS 1.2 or newer.
  • Run the bot as a systemd service with Restart=on-failure and load the token with LoadCredential=, which systemd recommends over environment variables for secrets.
  • Set the httpx logger to WARNING in python-telegram-bot: at INFO it writes every Bot API URL, token included, into your logs.
  • Only one process may poll a token at a time; a second one gets a 409 Conflict, so test with a separate bot.
  • A single text bot fits a 1 GB server, Ubuntu's documented minimum for cloud images; size up for databases, media or AI calls, and once the bot runs 24/7 it never costs more than the plan's monthly price in a billing period.
Long polling: the bot on the VPS opens an outbound HTTPS request to api.telegram.org. Webhook: api.telegram.org sends an HTTPS POST with a secret header to Caddy on port 443, which forwards it over plain HTTP to the bot on 127.0.0.1:8081.Long polling (getUpdates)Webhook (setWebhook)Your botapi.telegram.orgapi.telegram.orgCaddy :443Your boton the VPSoutbound HTTPSno domain, no open portTLS 1.2+127.0.0.1:8081Telegram to Caddy: HTTPS POST + secret headerCaddy to bot: plain HTTP on localhost

Long polling vs webhook: which should your bot use?

Telegram offers two mutually exclusive ways to receive updates, and the Bot API keeps undelivered updates on its servers for at most 24 hours either way. With long polling your bot asks Telegram for updates; with a webhook Telegram pushes each update to your server as an HTTPS POST. The differences that matter on a VPS:

Long polling (getUpdates)Webhook (setWebhook)
Who opens the connectionYour bot, outbound to api.telegram.orgTelegram, inbound to your server
Domain and TLS certificateNot neededNeeded: a certificate whose CN or SAN matches the host (a self-signed certificate may use the IP)
Inbound portNone443, 80, 88 or 8443 only
IP versionNo inbound address neededIPv4 only; IPv6 is not supported for webhooks
Parallel deliveryOne poller per token; a second one gets a 409 Conflictmax_connections 1 to 100 simultaneous HTTPS connections (default 40)
DebuggingErrors appear in your own logsgetWebhookInfo shows pending_update_count and last_error_message
Works behind NAT or on a laptopYesNo: Telegram must reach a public IPv4 address
Moving parts on the VPSBot onlyBot + reverse proxy + DNS + certificate renewal
Sources: Telegram Bot API (getUpdates, setWebhook, getWebhookInfo) and Telegram’s webhook guide, checked October 3, 2026.

Speed is rarely the deciding factor. Telegram’s webhook guide says a webhook may save CPU cycles and improve response time, but that “these things however depend heavily on the usage pattern of your bot”. A long poll already waits on an open request until an update arrives, so a small bot feels the same either way.

You use getUpdates at the moment and it works, keep it that way. […] There is nothing wrong with using getUpdates.

Telegram, Marvin’s Marvellous Guide to All Things Webhook

Choose long polling when:

  • you have no domain, or you don’t want to manage TLS certificates;
  • the bot runs as one process on one server (most bots);
  • you are testing on a short-lived hourly VPS that you will delete tonight.

Choose a webhook when:

  • the VPS already serves a website or API behind a reverse proxy, so ports 80 and 443 are open anyway;
  • you want Telegram to deliver updates in parallel (up to 100 connections) to a busy bot;
  • you plan to run several app instances behind one HTTPS endpoint.

The bot in this guide supports both modes from one file, so you can start with polling and switch later by changing two settings.

What does a Telegram webhook require?

Telegram’s webhook guide and Bot FAQ list the hard requirements. If one fails, delivery fails and getWebhookInfo records the reason.

  • Ports: 443, 80, 88 or 8443. Other ports “are not supported and will not work”.
  • IPv4: “IPv6 is currently not supported for webhooks”, so the hostname needs an A record.
  • TLS 1.2 or newer: SSLv2/3, TLS 1.0 and TLS 1.1 are rejected. Plain-HTTP webhooks are not possible on any port.
  • A matching certificate: the CN or a SAN must match the domain you register, with the full intermediate chain. The FAQ warns that wildcard certificates may not be supported.
  • No redirects: the URL you register must answer directly, so register the final HTTPS URL.
  • Source ranges: webhook requests come from 149.154.160.0/20 and 91.108.4.0/22, and Telegram notes the range “might change in the future”.

Tip: Verify webhook requests with secret_token instead of an IP allowlist. When you set it, Telegram sends it in the X-Telegram-Bot-Api-Secret-Token header of every request (1 to 256 characters, A-Z, a-z, 0-9, _ and -). python-telegram-bot answers 403 Forbidden to any request without the right header, and you avoid breaking delivery when Telegram’s IP ranges change.

Note: Telegram’s Bot API server is open source (tdlib/telegram-bot-api). Per the Bot API docs, a copy you run yourself downloads files of any size (the cloud limit is 20 MB), uploads up to 2,000 MB (cloud: 50 MB) and accepts webhooks on any port, over plain HTTP or to a local IP. You must call logOut first, and the bot cannot log back in to the cloud server for 10 minutes. Most bots never need this.

How much RAM and CPU does a Telegram bot need?

Neither Telegram nor python-telegram-bot publishes a RAM requirement, so size from two documented limits. First, Ubuntu Server 24.04 lists 1 GB of RAM as the minimum for cloud images (3 GB or more as the suggested minimum for general server use). Second, Telegram’s Bot FAQ caps what a bot can send: about 1 message per second in a single chat, 20 messages per minute in a group, and about 30 messages per second for bulk notifications unless you pay for broadcasts.

So the bot process is rarely the bottleneck; what your handlers do decides the plan: a database, media processing or calls to an LLM API. The table is reasoning, not a benchmark.

Bot profileWhat uses the resourcesStarting planMonthly cap
Personal, alert or notification bot; long polling; no databaseOne Python process, mostly idleQuartz Q1: 1 vCPU, 1 GB RAM$5.00/month cap
Group or community bot with SQLite or Redis; webhook behind CaddyBot + Caddy + a small datastoreQuartz Q2: 1 vCPU, 2 GB RAM$10.00/month cap
Several bots, PostgreSQL or Docker, image handling, AI agent front endsSeveral processes, short CPU burstsQuartz Q4: 2 vCPU, 4 GB RAM$15.00/month cap
Sustained CPU work such as transcoding or local inferenceConstant CPU loadChrono C8: 2 dedicated vCPU, 8 GB RAM$35.00/month cap
Sizing reasoning from Ubuntu’s documented minimum and Telegram’s flood limits. The monthly cap is the most one server is charged in a billing period; below it you pay by the hour. Prices render from the live HourlyVPS price list.

After a day of traffic, systemctl status tgbot shows the bot’s real memory use on its Memory: line. Running a Discord bot too? The Discord bot hosting guide uses the same systemd pattern, so both can share one server.

Does server location matter for a Telegram bot?

Your users talk to Telegram, not to your VPS. The latency you control is your server’s leg to api.telegram.org plus the leg to any API your handlers call. The Bot API docs do not say where api.telegram.org is served from, so measure it from the server you plan to use, such as an hourly test server in our Istanbul location:

curl -s -o /dev/null -w 'connect %{time_connect}s  tls %{time_appconnect}s  total %{time_total}s\n' https://api.telegram.org

Repeat it a few times, do the same against your database or LLM provider, and compare the sum with your current host. On an hourly server, running this check for one hour costs $0.01. The VPS location guide covers the method in depth.

What does a Telegram bot VPS cost per month?

A bot that must answer at 3 a.m. runs 24/7, and that is where the monthly cap matters: the server is billed by the hour, but its charges stop at the plan’s monthly price in each billing period (one month from your order date), so a live bot is in effect a monthly VPS with no contract and no monthly prepayment. The same hourly billing keeps the work before launch cheap: build, test and measure for a few hours, then delete the server. The table shows Quartz Q1 at each duration, with the cap applied once it is reached.

Quartz Q1 (1 GB): total cost by duration, billed by the hour and capped at the monthly price
DurationHours on the meterCost $0.01/hour · cap $5.00/monthNote
1 hour1$0.01
8 hours8$0.08
1 day24$0.24
7 days168$1.68
30 days720$5.00Capped at the monthly price
Charges stop at $5.00 after 500 hours (about 20.8 days) in a billing period; the rest of that period is free.

Billing rules: Billing is by the hour: every hour a server exists is charged at the plan’s hourly rate. The price tapes and cost tables on this site count every started hour as a full hour, so they show the most a duration can cost; the cost calculator charges a partial hour to the nearest cent, as the bill does. Every server is billed by the hour: the plan’s hourly rate is deducted from the server’s prepaid balance for every hour it exists, powered on or off, until you delete it. A stopped server is still billed, because its vCPU, memory, disk and IP addresses stay reserved for you; only deleting the server stops billing. Each new server is ordered with an initial credit, prepaid and used for that server’s hours, and account top-ups start at $5.

Traffic rarely matters for a text bot. Istanbul plans include the monthly traffic allowance listed for each plan, prorated for a server that exists for part of a billing period. A bot that sends large media should check its plan’s allowance first. See why capped hourly billing never costs more than monthly, price your own mix with the VPS cost calculator, compare every size on the pricing page, or check what a VPS costs across 20 providers.

What about free hosting?

Free platform tiers can host a hobby bot if you accept their limits. As of October 2026, Render’s documentation says it spins down a free web service after 15 minutes without inbound traffic and that spinning back up “takes about one minute”. Render offers free instances only for web services, static sites, Postgres and Key Value, so a background worker, the service type a polling bot would use, is not free.

So a webhook bot on a free web service answers the first message after an idle spell about a minute late. A free tier fits a demo bot few people use. A VPS fits a bot people rely on, long polling, or anything that keeps state on disk.

How to deploy a Telegram bot on a VPS (Ubuntu 24.04)

These steps assume a fresh Ubuntu 24.04 LTS server, a sudo user and SSH keys. If you are not there yet, connect to the VPS over SSH and work through the new-server security checklist first. Debian 12 works the same way: it ships Python 3.11 and systemd 252, which both meet the requirements below.

python-telegram-bot or aiogram?

Both main Python libraries are async and need Python 3.10 or newer; Ubuntu 24.04 ships Python 3.12.

LibraryVersion (Oct 2026)Bot API levelWebhook serverPython
python-telegram-bot22.8 (June 12, 2026)10.0Built in: run_webhook with the [webhooks] extra (Tornado)3.10+
aiogram3.31.0 (August 26, 2026)10.3aiohttp integration (SimpleRequestHandler)3.10 to 3.14
Sources: python-telegram-bot documentation and the aiogram PyPI page. The Bot API itself was at version 10.3 (August 24, 2026) on October 3, 2026.

This guide uses python-telegram-bot because run_polling() and run_webhook() handle the webhook bookkeeping for you: polling mode always calls deleteWebhook at startup, and webhook mode always calls setWebhook. The systemd and Caddy parts below work unchanged for aiogram or a Node.js bot; only ExecStart changes. Prefer containers? Set up Docker with the Docker install guide and pass the token as a Compose secret: Docker mounts it at /run/secrets/bot_token, so setting CREDENTIALS_DIRECTORY=/run/secrets lets the same bot.py read it.

1. Create the bot and a test twin

In Telegram, open @BotFather, send /newbot and choose a name and a username ending in bot. BotFather replies with the token, which “can be used by anyone to control your bot”, in Telegram’s words. Create a second bot for testing too, as Telegram’s bot features page suggests: your laptop polls the test bot while the VPS polls the real one, with no 409 conflicts.

2. Create a service user and a virtual environment

Install the venv module, which Ubuntu packages separately:

sudo apt update && sudo apt install -y python3-venv

Create a system user with no login shell. The bot runs as this user, never as root:

sudo useradd --system --create-home --home-dir /opt/tgbot --shell /usr/sbin/nologin tgbot

Create the virtual environment and install the library, pinned to the version this guide was checked against:

sudo -H -u tgbot python3 -m venv /opt/tgbot/venv
sudo -H -u tgbot /opt/tgbot/venv/bin/pip install "python-telegram-bot[webhooks]==22.8"

3. Store the token where only systemd can read it

Many tutorials put the token in a .env file and load it with EnvironmentFile=. The systemd manual advises against environment variables for secrets: variables set in a unit are visible to unprivileged clients over D-Bus, and every environment variable is inherited by child processes, including across security boundaries. It recommends LoadCredential= instead, which hands the service a read-only copy that only the service user and root can read. Ubuntu 24.04 ships systemd 255, and the setting has existed since version 247.

Create a root-only directory, then write the token without it touching your shell history. Paste the token, press Enter, then Ctrl+D:

sudo install -d -m 700 /etc/tgbot
sudo sh -c 'umask 077; cat > /etc/tgbot/bot_token'

Warning: Never commit the token to Git or paste it into the unit file. If it leaks, send /token to @BotFather to generate a new one (the command named in Telegram’s BotFather guide), write it to /etc/tgbot/bot_token again and restart the service.

4. Write the bot

Create the file as root. The bot only needs to read its code, and ProtectSystem=strict in the unit below keeps /opt read-only for the running service:

sudo nano /opt/tgbot/bot.py

Paste this echo bot. It reads the token from the systemd credential directory, falls back to a BOT_TOKEN environment variable when you run it on your laptop, and picks its mode from BOT_MODE:

import logging
import os
from pathlib import Path

from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters

logging.basicConfig(format="%(levelname)s %(name)s: %(message)s", level=logging.INFO)
# httpx logs every request URL at INFO, and Bot API URLs contain your token.
logging.getLogger("httpx").setLevel(logging.WARNING)


def read_secret(name: str) -> str:
    """Read a systemd credential; fall back to an env var for local runs."""
    cred_dir = os.environ.get("CREDENTIALS_DIRECTORY")
    if cred_dir and (Path(cred_dir) / name).is_file():
        return (Path(cred_dir) / name).read_text().strip()
    return os.environ[name.upper()]


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.effective_message.reply_text("Hi! I run on a VPS. Send me any text.")


async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.effective_message.reply_text(update.effective_message.text)


def main() -> None:
    app = Application.builder().token(read_secret("bot_token")).build()
    app.add_handler(CommandHandler("start", start))
    # UpdateType.MESSAGE skips edited messages, which have no update.message
    app.add_handler(
        MessageHandler(filters.UpdateType.MESSAGE & filters.TEXT & ~filters.COMMAND, echo)
    )

    if os.environ.get("BOT_MODE", "polling") == "webhook":
        app.run_webhook(
            listen="127.0.0.1",
            port=8081,
            url_path="telegram",
            webhook_url=os.environ["WEBHOOK_URL"],
            secret_token=read_secret("webhook_secret"),
            allowed_updates=Update.ALL_TYPES,
        )
    else:
        app.run_polling(allowed_updates=Update.ALL_TYPES)


if __name__ == "__main__":
    main()

Three lines do more than they appear to:

  • logging.getLogger("httpx").setLevel(logging.WARNING): the library sends requests through httpx, which logs every request URL at INFO level. Bot API URLs look like https://api.telegram.org/bot<token>/METHOD, so without this line your token lands in the journal. The library’s own examples include it.
  • filters.UpdateType.MESSAGE: a MessageHandler also matches edited messages, where update.message is None. This filter plus effective_message avoids an error every time someone edits a message.
  • listen="127.0.0.1": in webhook mode the bot listens on localhost only. Caddy handles TLS on port 443, so the bot’s own port (8081) need not be one of Telegram’s four.

How to keep a Telegram bot running 24/7 with systemd

A systemd service starts the bot at boot, restarts it after a crash and sends its output to the journal; our guide to creating a systemd service explains each directive in more depth. Create the unit file:

sudo nano /etc/systemd/system/tgbot.service
[Unit]
Description=Telegram bot (python-telegram-bot)
Wants=network-online.target
After=network-online.target

[Service]
User=tgbot
Group=tgbot
WorkingDirectory=/opt/tgbot
ExecStart=/opt/tgbot/venv/bin/python /opt/tgbot/bot.py
Environment=PYTHONUNBUFFERED=1
LoadCredential=bot_token:/etc/tgbot/bot_token
StateDirectory=tgbot
Restart=on-failure
RestartSec=5
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

What each hardening line does, per the systemd.exec manual:

  • LoadCredential=bot_token:/etc/tgbot/bot_token copies the token into the directory named by $CREDENTIALS_DIRECTORY, readable only by the tgbot user and root.
  • ProtectSystem=strict mounts the whole file system read-only for the bot, so it cannot modify its own code or the OS.
  • StateDirectory=tgbot creates /var/lib/tgbot, owned by tgbot and writable despite ProtectSystem=strict. Keep SQLite files and other state there.
  • ProtectHome=yes, PrivateTmp=yes and NoNewPrivileges=yes hide /home, give the bot a private /tmp and block privilege escalation.
  • Restart=on-failure with RestartSec=5 restarts the bot 5 seconds after a crash, but not after a clean stop.

Load the unit, start it now and enable it at boot:

sudo systemctl daemon-reload
sudo systemctl enable --now tgbot

Follow the log while you message the bot from Telegram:

sudo journalctl -u tgbot -f

You should see Application started and no lines that contain your token. Then prove it survives a reboot: run sudo reboot, reconnect, and check that systemctl is-active tgbot prints active.

Update and redeploy the Telegram bot

Edit the code, restart the service and read the newest log lines:

sudo nano /opt/tgbot/bot.py
sudo systemctl restart tgbot
sudo journalctl -u tgbot -n 30 --no-pager

A restart loses no messages: Telegram stores incoming updates until the bot receives them, for up to 24 hours. To upgrade python-telegram-bot, read its changelog, run the same pip install command with the new version number and restart. Pinning means upgrades happen when you choose, not on the next rebuild.

Switch to a webhook behind Caddy

Polling is complete as it stands. Switch to a webhook only if one of the reasons above applies. Caddy fits Telegram’s requirements by default: it obtains and renews certificates from a public ACME CA such as Let’s Encrypt automatically, and its default minimum protocol is TLS 1.2. Install it from Caddy’s official apt repository as shown in the Caddy reverse proxy guide, then work through five steps.

1. DNS. Create an A record such as bot.example.com that points to the server’s IPv4 address. Telegram does not deliver webhooks over IPv6.

2. Firewall. Caddy needs ports 80 and 443 open for certificate challenges and the HTTP redirect, and Telegram posts to 443. If UFW is active:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

3. Caddyfile. Add this site block to /etc/caddy/Caddyfile (on a fresh install, replace the default contents). Only the webhook path reaches the bot; every other path on that hostname gets a 404:

bot.example.com {
	handle /telegram {
		reverse_proxy 127.0.0.1:8081
	}
	handle {
		respond 404
	}
}
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

4. Secret. Hex output uses only characters secret_token allows, and 64 characters is well under the 256 limit:

sudo sh -c 'umask 077; openssl rand -hex 32 > /etc/tgbot/webhook_secret'

5. Mode. Open a drop-in for the service, add these lines with your hostname and save. systemctl edit reloads systemd when the editor closes:

sudo systemctl edit tgbot
[Service]
Environment=BOT_MODE=webhook
Environment=WEBHOOK_URL=https://bot.example.com/telegram
LoadCredential=webhook_secret:/etc/tgbot/webhook_secret

Confirm that systemd sees the override, then restart. systemctl cat tgbot must print the drop-in file with your three lines:

systemctl cat tgbot
sudo systemctl restart tgbot

Check it from the outside first. A JSON POST without the secret header must get 403, and any other path must get 404. That proves the TLS path, the Caddy route and the secret check. Keep the JSON header: python-telegram-bot also answers 403 to any request without Content-Type: application/json, so a bare POST would pass even with no secret set.

curl -s -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' -d '{}' https://bot.example.com/telegram
curl -s -o /dev/null -w '%{http_code}\n' https://bot.example.com/

Then ask Telegram what it sees. This reads the token from the root-only file and passes it to curl on standard input, so it never appears in your shell history or the process list:

sudo sh -c 'printf "url = \"https://api.telegram.org/bot%s/getWebhookInfo\"\n" "$(cat /etc/tgbot/bot_token)"' | curl -s -K - | python3 -m json.tool

A healthy webhook shows your URL, a pending_update_count near 0 and no last_error_message. To go back to polling, run sudo systemctl revert tgbot (it removes the drop-in and keeps your unit), then sudo systemctl daemon-reload and restart the service: polling mode deletes the webhook on startup.

Troubleshooting: the errors you will actually see

SymptomLikely causeFix
409 Conflict: terminated by other getUpdates request; make sure that only one bot instance is runningTwo processes poll with the same token, often a laptop copy plus the VPSStop the other copy. Use a separate test bot for local work.
409 Conflict: can't use getUpdates method while webhook is activeA webhook is still set; getUpdates does not work while one existsCall deleteWebhook; python-telegram-bot’s polling mode does this at startup.
pending_update_count keeps growingTelegram cannot reach or verify your endpointRead last_error_message, check the A record, ports 80 and 443, and sudo journalctl -u caddy.
Certificate or SSL error in last_error_messageCN or SAN mismatch, wildcard certificate or incomplete chainLet Caddy issue a certificate for the exact hostname you registered.
Every webhook request gets 403The webhook was registered again without this secret, for example by a manual setWebhook call or another copy of the botStop the other copy, then restart the bot so run_webhook registers the current secret.
Bot ignores most group messagesPrivacy mode, on by defaultIn privacy mode a bot sees only commands meant for it and replies to its messages. Turn it off with /setprivacy in @BotFather and re-add the bot to the group, or make the bot an admin.
429 Too Many RequestsFlood limits: about 1 msg/s per chat, 20 msg/min per group, ~30 msg/s overallWait for the retry_after seconds in the response; spread broadcasts out.
Bot answers old messages after downtimeTelegram queued up to 24 hours of updatesPass drop_pending_updates=True to run_polling() or run_webhook() if stale updates should be skipped.
Service restarts every 5 seconds; the journal shows InvalidToken and “was rejected by the server”Wrong, truncated or revoked tokenRewrite /etc/tgbot/bot_token and run sudo systemctl restart tgbot.
Error texts from the source of Telegram’s Bot API server (tdlib/telegram-bot-api); behaviors from the Telegram Bot API, Bot FAQ and python-telegram-bot documentation.

Go-live checklist

  1. SSH keys only, root login off and a firewall that opens nothing beyond SSH (plus 80 and 443 for a webhook).
  2. Token in /etc/tgbot/bot_token with mode 600, loaded with LoadCredential=; never in Git, the unit file or the environment.
  3. The httpx logger at WARNING, and sudo sh -c 'journalctl -u tgbot | grep -c -F -f /etc/tgbot/bot_token' prints 0.
  4. Exactly one process per token, with a separate bot for testing.
  5. Commands that change data or reveal anything private check update.effective_user.id against your own user ID: anyone who finds the bot’s username can message it.
  6. systemctl is-enabled tgbot prints enabled, and the bot came back after a reboot.
  7. For a webhook: getWebhookInfo shows no last_error_message.
  8. State kept in /var/lib/tgbot and covered by a snapshot or backup.
  9. A broadcast plan that respects Telegram’s limits and the acceptable use policy: no unsolicited bulk messages.

Deploy this setup

Run this Telegram bot 24/7, with room for Caddy and SQLite

Quartz Q2 · 1 shared vCPU · 2 GB RAM · 50 GB NVMe · Istanbul

  • Per hour$0.02/hour
  • Per day (24 h)$0.48/day
  • Monthly cap$10.00/monthFor this job
Deploy Quartz Q2

Starts with a $5 initial credit, which goes into the server’s balance and pays for its hours.

Billed by the hour, never more than $10.00 per billing period. Delete the server and billing stops.

FAQ

Do I need a domain to run a Telegram bot on a VPS?

Not for long polling: the bot only makes outbound HTTPS requests to api.telegram.org. A webhook needs an HTTPS URL with a matching certificate, which in practice means a domain, although Telegram also accepts a self-signed certificate that uses the server's IP address as its CN.

Which ports does a Telegram webhook support?

Ports 443, 80, 88 and 8443, always over HTTPS with TLS 1.2 or newer and only over IPv4, per the Bot API documentation checked on October 3, 2026. Behind a reverse proxy, the bot process itself can listen on any local port.

How much RAM does a Telegram bot need?

A single text bot fits a 1 GB server, which is Ubuntu Server 24.04's documented minimum for cloud images. Plan 2 GB if you add Caddy and a database, and more for media processing or several bots.

Can one VPS run several Telegram bots?

Yes. Each bot is one process with its own token, so give each its own systemd unit and credential file; for webhooks, route each bot to its own path or hostname in Caddy and its own local port. Never run the same token twice in polling mode: Telegram answers the second poller with 409 Conflict.

What happens to messages while my bot is offline?

Telegram keeps undelivered updates for up to 24 hours and delivers them when the bot reconnects. Anything older is lost, so a bot that must not miss messages needs an always-on server.

Is a webhook faster than long polling?

Usually not in a way users notice. A long poll waits on an open request until an update arrives, and Telegram's own guide says any webhook speed gain depends heavily on the bot's usage pattern.

Can I host a Telegram bot for free?

For testing, yes: long polling works from any computer while it is on. Free platform tiers come with limits; as of October 2026, Render's free web services spin down after 15 minutes without traffic, so a bot people rely on belongs on an always-on server.

Sources

  1. Telegram Bot API (getUpdates, setWebhook, getWebhookInfo, local Bot API server)Telegram · core.telegram.org · checked
  2. Marvin's Marvellous Guide to All Things WebhookTelegram · core.telegram.org · checked
  3. Bots FAQ (webhook problems, broadcasting limits)Telegram · core.telegram.org · checked
  4. Telegram Bot Features: BotFather, privacy mode, testingTelegram · core.telegram.org · checked
  5. python-telegram-bot v22.8: telegram.ext.Application (run_polling, run_webhook)python-telegram-bot · docs.python-telegram-bot.org · checked
  6. aiogram on PyPI (3.31.0)Python Package Index · pypi.org · checked
  7. systemd.exec: credentials, ProtectSystem=, StateDirectory=freedesktop.org (systemd) · freedesktop.org · checked
  8. Caddy: Automatic HTTPSCaddy · caddyserver.com · checked
  9. Ubuntu Server: System requirementsCanonical · ubuntu.com · checked
  10. Render: Deploy for FreeRender · render.com · checked
All posts