Milad Mobaseri
Isometric diorama of a small server whose Telegram bot connects out through a tunnel
Python

Running a Telegram Bot on an Iranian Server: A Stable Proxy Setup

Your Iranian server cannot reach Telegram? A stable SSH tunnel with autossh, aiogram 3 through a proxy, the bot under systemd, and when a webhook is worth it.

Short answer: a server in Iran can't reach api.telegram.org directly, so the bot has to send its requests through a proxy. The simplest stable setup: an SSH tunnel to a small server abroad, kept alive by autossh; the bot uses long polling through that tunnel; and both run under systemd so they come back on their own when they fail. A webhook only helps if Telegram can reach your domain from outside.

A miniature server with a Telegram bot connected to the outside through a tunnel

Why a server in Iran at all?

If your users are in Iran, you use an Iranian payment gateway, or your database and site already live there, a local server is cheaper and closer. There is only one problem: talking to Telegram itself. So the whole idea is to route only Telegram traffic abroad and keep everything else on this server.

Step 1: build a stable tunnel

A small VPS abroad is enough (the cheapest plan works). On the Iranian server:

sudo apt install autossh
ssh-keygen -t ed25519 -f ~/.ssh/tunnel -N ""
ssh-copy-id -i ~/.ssh/tunnel.pub user@FOREIGN_IP

Now define the tunnel as a service. File /etc/systemd/system/tg-tunnel.service:

[Unit]
Description=SOCKS tunnel for Telegram
After=network-online.target

[Service]
User=botuser
Environment=AUTOSSH_GATETIME=0
ExecStart=/usr/bin/autossh -M 0 -N -D 127.0.0.1:1080 \
  -o ServerAliveInterval=20 -o ServerAliveCountMax=3 \
  -o ExitOnForwardFailure=yes -i /home/botuser/.ssh/tunnel user@FOREIGN_IP
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now tg-tunnel
curl -s --socks5-hostname 127.0.0.1:1080 https://api.telegram.org | head -c 100

If the last command answers, the tunnel works. ServerAliveInterval detects a dead connection quickly, and Restart=always reconnects it when it drops.

Bind port 1080 to 127.0.0.1 only, never 0.0.0.0, or anyone can use your proxy.

Step 2: connect the bot to the proxy (aiogram 3)

pip install aiogram aiohttp-socks
import asyncio, os
from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession

session = AiohttpSession(proxy="socks5://127.0.0.1:1080")
bot = Bot(os.environ["BOT_TOKEN"], session=session)
dp = Dispatcher()

# your handlers here

async def main():
    await bot.delete_webhook(drop_pending_updates=False)
    await dp.start_polling(bot, polling_timeout=30)

asyncio.run(main())

Two things matter:

  • Only the bot goes through the proxy. Other requests (payment gateway, local APIs) go direct, which is exactly what you want.
  • Call delete_webhook at startup. If a webhook was set earlier, polling receives nothing and you won't know why for a long time.

Step 3: run the bot as a service too

File /etc/systemd/system/mybot.service:

[Unit]
Description=Telegram bot
After=tg-tunnel.service
Requires=tg-tunnel.service

[Service]
User=botuser
WorkingDirectory=/home/botuser/bot
EnvironmentFile=/home/botuser/bot/.env
ExecStart=/home/botuser/bot/.venv/bin/python main.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Requires means the bot won't start without the tunnel, and restarts with it. Logs are always at hand:

journalctl -u mybot -f

What about webhooks?

A webhook means Telegram sends requests to your server. On an Iranian server that has two problems:

  1. Telegram must reach your domain from outside, which is not always reliable, especially during disruptions.
  2. setWebhook itself and replying to users still need the proxy.

So for a server in Iran, polling through the tunnel is simpler and more stable. A webhook is worth it when traffic is high and your domain is reachable from outside (for example behind Cloudflare). Then always set a secret_token so nobody can send fake requests to your webhook URL.

When it doesn't work

Symptom Likely cause
Cannot connect to host api.telegram.org The tunnel is down: systemctl status tg-tunnel
Bot is running but receives nothing An old webhook is still active; call delete_webhook
TelegramConflictError Another copy of the bot is polling somewhere else
Drops every few hours No ServerAliveInterval, or the foreign server is unstable

Checklist before you relax

  • The tunnel is active (systemctl status tg-tunnel) and comes back after a server reboot.
  • The proxy listens only on 127.0.0.1, not on every IP.
  • The bot depends on the tunnel via Requires and runs with Restart=always.
  • The bot token lives in .env, not in the code or in git.
  • Restart the tunnel on purpose once and check the bot answers again without you.

If all five are ticked, your bot runs on the Iranian server with almost no babysitting.

FAQ

Can I use V2Ray or a ready-made proxy instead of an SSH tunnel?
Yes. Anything that exposes a SOCKS5 or HTTP proxy on 127.0.0.1 works with the same code; just change the proxy address. I suggest the SSH tunnel because it needs no extra tools and is easy to run as a service.

What if the internet goes national-only?
The Iranian server loses its link abroad and this setup stops too. Only bots running on a foreign server keep working.

I use python-telegram-bot, not aiogram.
It supports proxies too (install python-telegram-bot[socks]). When building the Application, set both .proxy(...) and .get_updates_proxy(...); forget the second and the bot can send but never receives.

#aiogram#linux#proxy#telegram bot

Related posts

Python

Python Automation: How to Automate Repetitive Tasks

Spending too much time on repetitive tasks? From organizing files and merging spreadsheets to scheduled reports, plus six lessons I only learned from real automation.

10 min read35 views
Read more →