Cómo alojar un bot de Telegram que esté siempre en línea
Sigue esto en tu propio servidor. Empieza gratis, sin tarjeta
Un bot de Telegram que solo responde mientras tu computadora está abierta todavía no es un bot, es un script. La brecha entre los dos es la misma que para cualquier otro servicio siempre activo: un proceso que sigue corriendo, que reinicia si falla, y que sigue preguntando a Telegram por actualizaciones o tiene algún lugar público donde Telegram pueda enviarlas. Esta guía construye un bot real con python-telegram-bot, recorre la única decisión que realmente importa para alojarlo (long polling contra webhook), y cubre el Dockerfile, la política de reinicio y el camino de despliegue para mantenerlo en línea.
Lo que necesitas
- Python 3.10 o más reciente instalado localmente (python-telegram-bot 22.x lo requiere).
- Una cuenta de Telegram y @BotFather para crear el bot y obtener un token.
- Docker instalado localmente, para construir y probar la imagen.
- Un servidor donde correr el contenedor, o una cuenta de Railyard para el atajo de la última sección.
Paso 1: crea el bot y obtén un token
Abre un chat con @BotFather en Telegram, envía /newbot, dale un nombre y un nombre de usuario que termine en bot. BotFather responde con un token que se ve como 123456789:AAFkj2-ejemplofalso. Ese token es la credencial completa para controlar el bot; va en una variable de entorno, nunca subido a un repositorio.
Paso 2: escribe el bot
python-telegram-bot 22.8 es la versión actual en PyPI. Instálalo:
python -m venv venv
source venv/bin/activate
pip install "python-telegram-bot==22.8"
bot.py:
import logging
import os
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters
logging.basicConfig(format="%(asctime)s %(name)s %(levelname)s %(message)s", level=logging.INFO)
logger = logging.getLogger(__name__)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text("Hola. Mándame lo que sea y te lo devuelvo.")
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(update.message.text)
def main() -> None:
token = os.environ["TELEGRAM_BOT_TOKEN"]
application = Application.builder().token(token).build()
application.add_handler(CommandHandler("start", start))
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()
Pruébalo localmente:
TELEGRAM_BOT_TOKEN=tu-token-aqui python bot.py
Mándale un mensaje al bot en Telegram; /start y cualquier texto simple deberían recibir una respuesta en uno o dos segundos.
Paso 3: long polling contra webhook, y por qué importa para el hosting
application.run_polling() de arriba es long polling: tu bot le pregunta repetidamente a los servidores de Telegram “¿hay algo nuevo para mí?” mediante una conexión HTTP que Telegram mantiene abierta y responde en cuanto existe una actualización, y entonces el bot vuelve a preguntar de inmediato. Nada en tu bot necesita ser alcanzable desde internet; solo hace solicitudes salientes hacia Telegram, la misma forma que la conexión al gateway de Discord en alojar un bot de Discord las 24 horas. Por eso el long polling es la opción correcta por defecto para “siempre en línea”: solo necesita una cosa, un proceso que siga corriendo y preguntando, sin nada que exponer, sin dominio, y sin certificado.
La alternativa, un webhook, invierte la dirección: registras una URL HTTPS pública con Telegram (application.run_webhook(...), escuchando en un puerto, más una llamada para configurar la URL del webhook del lado de Telegram), y Telegram envía cada actualización a esa URL como un POST HTTP en el momento en que ocurre, en vez de que tu bot la pida. El compromiso genuinamente honesto: un webhook elimina el ida y vuelta constante del polling y puede ser marginalmente más rápido, pero no es realmente más simple de alojar, es un conjunto de requisitos diferente. Necesita un dominio real con un certificado TLS válido (Telegram exige HTTPS para webhooks, autofirmado solo con pasos extra que la mayoría de los bots evita), y de forma crítica, igual necesita un proceso siempre corriendo para recibirlo: un webhook se dispara por HTTP, no se dispara-y-destruye por solicitud, porque si nada está escuchando en la URL registrada cuando Telegram intenta entregar una actualización, esa actualización simplemente se pierde. “Basado en HTTP” no significa “funciona en una plataforma que escala a cero” aquí, ya que el bot igual tiene que estar arriba en el momento exacto e impredecible en que Telegram envía algo.
Para un bot que alojas tú mismo sin ya tener un dominio y un certificado configurados, el long polling es la opción más simple, e igualmente “siempre en línea”, y es sobre la que se construye esta guía. Cambia a un webhook más adelante si el volumen de solicitudes realmente lo justifica; las funciones manejadoras (start, echo) no cambian, solo cambia main().
Paso 4: escribe el Dockerfile
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd --create-home appuser
USER appuser
CMD ["python", "bot.py"]
requirements.txt:
python-telegram-bot==22.8
Igual que con el Dockerfile del bot de Discord, aquí no hay EXPOSE: el long polling solo hace conexiones salientes, así que no hay nada que necesite aceptar una entrante. La imagen corre como un appuser no root creado explícitamente, el equivalente en Python de la historia del usuario node para una imagen de Node.
Paso 5: política de reinicio y logging
docker build -t my-telegram-bot .
docker run -d --name my-telegram-bot --restart unless-stopped \
-e TELEGRAM_BOT_TOKEN=tu-token-aqui \
my-telegram-bot
--restart unless-stopped reinicia el contenedor tras un fallo o un reinicio de la máquina host, sin que nadie entre por SSH. La llamada a logging.basicConfig del Paso 2 envía todo a stdout, que Docker ya captura:
docker logs -f my-telegram-bot
Deberías ver las líneas INFO que python-telegram-bot emite por sí solo al arrancar (confirmando que se conectó a la API de Telegram) y una por cada /start o mensaje devuelto en eco.
El atajo: hazlo con Railyard
Apunta Railyard al mismo repositorio. Un Dockerfile que construye una imagen autocontenida (no una que espera un directorio fuente montado con bind mount, algo que la detección de build de Railyard omite automáticamente) se construye y corre directamente; consulta los métodos de build. Igual que el bot de Discord, esta es una app solo-worker sin proceso web, que Railyard corre igual que cualquier otra, solo que sin puerto asociado; consulta Workers y tareas programadas.
Agrega TELEGRAM_BOT_TOKEN en la pestaña Variables de la app; se cifra y se aplica en el siguiente despliegue, reemplazando la bandera -e del Paso 5. Consulta variables y secretos. Deja Sleep when idle (bajo Settings → General) apagado para este proceso: un chat tranquilo puede parecer inactivo en CPU por largos periodos mientras el bot está en medio de un long poll, esperando que respondan los servidores de Telegram, y esa espera es exactamente lo que no debería interrumpirse.
La política de reinicio del Paso 5 tampoco es un ajuste que se configure por separado, es simplemente cómo corre el contenedor de cada app en Railyard por defecto, el mismo comportamiento de no detenerse salvo que tú lo detengas. Los logs están en la pestaña Logs de la página de la app, o con railyard logs my-bot --follow desde la CLI.
Si prefieres el enfoque de webhook del Paso 3 en vez de long polling, el HTTPS automático de Railyard (cada app recibe un certificado de inmediato, un dominio personalizado agrega un registro DNS y una verificación de propiedad; consulta dominios y HTTPS) cubre la única parte que de otro modo hace que los webhooks sean la opción más complicada de autoalojar; la app entonces necesitaría un pequeño proceso web además de la lógica del bot para recibir las solicitudes POST de Telegram, lo cual es un cambio de arquitectura real respecto a la forma solo-worker de esta guía, no solo un interruptor de configuración.
Solución de problemas
El bot no responde, pero el contenedor está corriendo. Revisa docker logs primero por un error de autorización; un token inválido falla en Application.builder().token(token).build() o poco después de que arranca run_polling(), y ese error casi siempre es lo primero en el log.
“Conflict: terminated by other getUpdates request”. Telegram solo permite una conexión de long polling por token de bot a la vez. Esto significa que dos procesos están corriendo con el mismo token simultáneamente, comúnmente una corrida de prueba local que se dejó corriendo mientras el mismo bot también está desplegado; detén uno de los dos.
El contenedor se reinicia en bucle. Revisa las primeras líneas de docker logs justo después de un reinicio, no el final. Una variable de entorno TELEGRAM_BOT_TOKEN faltante (un KeyError de os.environ["TELEGRAM_BOT_TOKEN"]) es la causa más común de fallo inmediato para un bot recién desplegado.
Los mensajes enviados mientras el bot estaba desconectado nunca llegan. Telegram sí pone en cola las actualizaciones para un bot brevemente inalcanzable y las entrega una vez que se reanuda el polling, por un tiempo limitado, pero un bot caído por un periodo extendido igual se perderá lo que quede fuera de esa ventana; esta es una razón para tratar “siempre en línea” como el objetivo real en vez de “generalmente en línea”, no como una red de seguridad en la que confiar.
Preguntas frecuentes
Long polling o webhook: ¿cuál debería usar realmente? Long polling, para la mayoría de los bots manejados por una persona o un equipo pequeño sin un dominio y certificado ya configurados. No necesita nada orientado al público. Cambia a un webhook solo cuando el volumen de solicitudes o la latencia realmente lo justifiquen, dados los requisitos adicionales de dominio, certificado y puerto entrante.
¿python-telegram-bot o node-telegram-bot-api: importa para el hosting? No para nada de lo que cubre esta guía. La versión mayor actual de node-telegram-bot-api (2.x) es un rediseño desde cero de su API comparado con versiones anteriores, pero la forma de hosting es idéntica en ambos casos: un proceso siempre activo, un Dockerfile sin puerto expuesto para el caso de long polling, y una política de reinicio. El código en Python y Node difiere; lo que el proceso necesita para mantenerse vivo no.
¿Puedo correr el bot gratis? La librería es gratis; un proceso siempre activo necesita cómputo siempre activo, para lo cual un nivel gratuito serverless generalmente no está pensado, ya que la mayoría escala a cero entre solicitudes. Un VPS pequeño a unos pocos dólares al mes es el piso realista para disponibilidad verdaderamente continua.
¿Qué pasa si dos instancias del bot corren al mismo tiempo con el mismo token? La API de long polling de Telegram rechaza explícitamente una segunda conexión getUpdates concurrente para el mismo token de bot con el error “Conflict” de la sección de solución de problemas de arriba; solo una instancia puede tener la conexión de polling a la vez, por diseño, lo cual también significa que no puedes escalar horizontalmente un bot de long polling como lo harías con una app web.
¿Necesito modo webhook para correr muchos bots o un bot con muchos usuarios? No necesariamente solo por el número de usuarios; el long polling escala según cuántos mensajes llegan, no según cuántos usuarios siguen al bot. El modo webhook se vuelve más atractivo a un volumen de mensajes genuinamente alto donde el propio ida y vuelta del polling agrega una sobrecarga significativa, lo cual es un umbral más alto del que alcanza la mayoría de los bots.
Mantenerse en línea, para un bot de Telegram, se reduce a las mismas dos cosas que para cualquier otro servicio que corre continuamente: un proceso que no se detiene solo, y uno que vuelve cuando algo lo detiene de todas formas. Todo lo demás, incluyendo si eliges polling o webhooks, es un detalle por encima de eso.
Por qué eligen Railyard para esto
- Tu propia cuenta en la nube. Despliega en DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, o cualquier servidor Linux por SSH, facturado directamente por tu proveedor, sin márgenes adicionales.
- Un precio fijo por equipo. El plan Free es gratis para siempre: un servidor más un servidor de base de datos, tres apps, sin tarjeta. Pro cuesta $19 al mes por hasta cinco servidores y apps ilimitadas. Team cuesta $49 al mes con servidores ilimitados. Sin cargos por app, por despliegue, por asiento ni por gigabyte.
- Plantillas de un clic. Despliega apps de código abierto, como bases de datos, analítica y herramientas de desarrollo, en tu propio servidor con un clic.
- Bases de datos administradas. Postgres, MySQL y Redis, con copias de seguridad programadas y verificadas por restauración, y recuperación a un punto en el tiempo.
- Despliegue con git push. Despliega desde GitHub, GitLab o la CLI, con HTTPS automático y lanzamientos sin tiempo de inactividad.