Alojar un bot de Discord en un VPS: en línea 24/7
- 18 de octubre de 2026
- 09:00
- Por Adrien Roche
- Tutoriales

Por qué un VPS es donde los bots de Discord se mantienen realmente en línea
Un bot de Discord no es una página web. Una vez conectado, mantiene una conexión WebSocket persistente con la gateway de Discord y debe responder continuamente a los heartbeats; cuando el proceso se detiene, el bot aparece desconectado y todos los comandos fallan. Ese único requisito, un proceso que nunca duerme, descarta la mayoría de los alojamientos «gratuitos»:
- Heroku eliminó sus dynos gratuitos en noviembre de 2022.
- Replit ya no mantiene los proyectos gratuitos en ejecución; el modo always-on pasó a los despliegues de pago.
- Glitch puso fin a su alojamiento de aplicaciones en 2025.
- Los planes web gratuitos que se duermen tras la inactividad cortan la conexión con la gateway cada vez que quedan inactivos; para los usuarios, ese bot simplemente está caído.
Alojar un bot de Discord en un VPS resuelve el problema de raíz: una máquina Linux con acceso root que funciona a todas horas, donde el bot es un servicio más gestionado por el sistema de init. Esta guía lo monta sobre Ubuntu 24.04 (válido también para 22.04 y Debian 12) con discord.py y discord.js, y después cubre la gestión del token, los reinicios, los logs, las actualizaciones y la memoria.
Preparar el VPS: un usuario dedicado y un runtime
Asegura primero la puerta de entrada: activa SSH en Ubuntu y refuérzalo con autenticación por clave, sin login de root y con ufw. Un bot se conecta hacia fuera, hacia Discord, y no necesita ningún puerto entrante, así que el firewall puede quedar cerrado a todo salvo SSH.
Paso 1: crear una cuenta sin privilegios para el bot
sudo useradd -r -m -d /opt/mybot -s /usr/sbin/nologin bot
Una cuenta dedicada implica que un bot con errores o una dependencia de npm o PyPI comprometida no puede leer tus claves SSH ni tocar otros servicios. Todo vive en /opt/mybot, propiedad de bot.
Paso 2 (Python): instalar discord.py en un entorno virtual
sudo apt update && sudo apt install -y python3 python3-venv python3-pip
sudo -u bot python3 -m venv /opt/mybot/.venv
sudo -u bot /opt/mybot/.venv/bin/pip install -U discord.py python-dotenv
discord.py 2.x requiere Python 3.8 o superior; Ubuntu 24.04 incluye la 3.12 y 22.04 la 3.10, así que el intérprete de la distribución sirve. Añade discord.py[voice] y ffmpeg solo para los canales de voz.
Paso 3 (Node.js): instalar una LTS actual y discord.js
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
sudo -u bot bash -c 'cd /opt/mybot && npm init -y && npm install discord.js dotenv'
El paquete nodejs propio de Ubuntu va muy por detrás del upstream y discord.js v14 necesita un runtime moderno, así que usa NodeSource (arriba) o nvm para una LTS actual. Con nvm, ejecuta which node y usa esa ruta absoluta en la unidad de servicio.
Mantener el token fuera del código: el archivo .env
El token del bot es una contraseña con control total sobre él: no lo pegues nunca en bot.py ni en index.js, ni lo subas a un repositorio. Colócalo en /opt/mybot/.env, legible solo por la cuenta del bot, usando un editor en lugar de echo, ya que un token escrito en la línea de comandos acaba en el historial del shell:
sudo -u bot nano /opt/mybot/.env # one line: DISCORD_TOKEN=your-token-here
sudo chmod 600 /opt/mybot/.env
echo ".env" | sudo -u bot tee -a /opt/mybot/.gitignore
El formato es un KEY=value por línea, sin comillas y sin export. python-dotenv y el paquete npm dotenv lo leen, y systemd puede inyectar el mismo archivo con EnvironmentFile= (siguiente sección); combinar ambos es inofensivo, porque ninguna de las dos bibliotecas sobrescribe una variable que ya existe. Si el token se filtra alguna vez, regenéralo de inmediato en el Developer Portal, en Bot → Reset Token.
Un bot mínimo en discord.py y discord.js
Ambos ejemplos leen el token del entorno, activan el intent de contenido de mensajes y responden a !ping. En el Developer Portal, crea una aplicación, añade un usuario bot, copia el token (se muestra una sola vez), activa Message Content Intent en Bot → Privileged Gateway Intents e invita al bot a un servidor de pruebas mediante el generador de URL de OAuth2 con el scope bot.
discord.py: /opt/mybot/bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f"Logged in as {client.user}")
@client.event
async def on_message(message):
if message.author == client.user:
return
if message.content == "!ping":
await message.channel.send("pong")
client.run(os.environ["DISCORD_TOKEN"])
discord.js: /opt/mybot/index.js
require("dotenv").config();
const { Client, GatewayIntentBits, Events } = require("discord.js");
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
client.once(Events.ClientReady, (c) => {
console.log(`Logged in as ${c.user.tag}`);
});
client.on(Events.MessageCreate, (message) => {
if (message.author.bot) return;
if (message.content === "!ping") message.reply("pong");
});
client.login(process.env.DISCORD_TOKEN);
Ejecútalo una vez en primer plano: sudo -u bot /opt/mybot/.venv/bin/python /opt/mybot/bot.py o sudo -u bot node /opt/mybot/index.js, espera a Logged in as, envía !ping y pulsa Ctrl-C. Ahora mismo el bot vive solo mientras dure tu sesión SSH: cerrar la terminal envía SIGHUP a sus procesos hijos y el bot muere.
Ejecutarlo bajo systemd con Restart=always
systemd ya es el sistema de init de Ubuntu, así que la forma más limpia de mantener el bot en línea tras cerrar sesión, tras un fallo y tras un reinicio es una unidad de servicio: sin ningún demonio extra, y con los logs en el journal.
Paso 1: escribir la unidad
sudo nano /etc/systemd/system/mybot.service
[Unit]
Description=Discord bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=bot
Group=bot
WorkingDirectory=/opt/mybot
EnvironmentFile=/opt/mybot/.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/mybot/.venv/bin/python /opt/mybot/bot.py
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/mybot
[Install]
WantedBy=multi-user.target
Para discord.js, pon ExecStart=/usr/bin/node /opt/mybot/index.js y elimina la línea PYTHONUNBUFFERED. Las directivas que importan:
Restart=alwaysreinicia el proceso ante cualquier salida, tanto un fallo como unsys.exit()limpio;RestartSec=5evita que un bot que muere al instante (token incorrecto, error de sintaxis) entre en un bucle cerrado.Wants=/After=network-online.targetretrasan el arranque hasta que la red esté disponible, para que el primer login tras un reinicio no falle por DNS.PYTHONUNBUFFERED=1: sin terminal, Python almacenastdouten búfer por bloques y las líneas deprint()llegan al journal con minutos de retraso o solo al salir.- Las líneas
Protect*,PrivateTmpyNoNewPrivilegesdejan el resto del sistema de archivos en solo lectura o invisible para el bot; añade aReadWritePaths=cualquier directorio en el que deba escribir.
Paso 2: habilitar, arrancar y verificar
sudo systemctl daemon-reload
sudo systemctl enable --now mybot
systemctl status mybot
sudo systemctl kill --signal=KILL mybot # simulate a crash; status is active again after 5 s
enable crea el enlace simbólico de arranque y --now inicia la unidad. La línea kill es la prueba de verdad: si systemctl status mybot muestra active (running) unos segundos después, la política de reinicio funciona, y un sudo reboot valida la ruta de arranque. Para verlo junto a todos los demás servicios, lista los servicios en ejecución con systemctl; aparece como mybot.service, y systemctl is-enabled mybot confirma que volverá tras un reinicio.
Alternativa: pm2, y cómo se compara con screen, nohup y Docker
Toda forma de mantener vivo un bot responde a tres preguntas: ¿sobrevive al cierre de sesión SSH, se reinicia tras un fallo y arranca al reiniciar la máquina?
| Método | Sobrevive al cierre de SSH | Se reinicia tras un fallo | Arranca al reiniciar | Veredicto |
|---|---|---|---|---|
nohup / & | Sí | No | No | Solo para pruebas rápidas. |
screen / tmux | Sí | No | No, salvo con un script | Depuración, no producción. |
| pm2 | Sí | Sí | Sí, tras pm2 startup + pm2 save | Bueno para equipos de Node.js; una sola CLI para logs y reinicios. |
| unidad systemd | Sí | Sí (Restart=) | Sí (enable) | Recomendado; sin software extra y con hardening incluido. |
Docker, restart: unless-stopped | Sí | Sí | Sí, con el demonio de Docker | Bueno cuando el bot ya se distribuye como imagen. |
pm2 es la opción popular en el mundo Node.js y también sirve para Python. Ejecútalo con la cuenta propietaria del bot:
sudo npm install -g pm2
cd /opt/mybot
pm2 start index.js --name mybot
pm2 start bot.py --name mybot --interpreter /opt/mybot/.venv/bin/python # Python variant
pm2 save
pm2 startup # prints a sudo command to run once
pm2 logs mybot
pm2 install pm2-logrotate # ~/.pm2/logs/ otherwise grows forever
pm2 startup genera una unidad systemd pm2-<user> que resucita al arrancar tu lista de procesos guardada, de modo que pm2 se apoya en systemd en lugar de sustituirlo: ese es el argumento honesto para usar systemd directamente con un solo bot. pm2 se gana su sitio con varios procesos Node o con un equipo que ya lo conoce.
Si prefieres los contenedores, el equivalente es restart: unless-stopped en un archivo Compose; el tutorial para instalar Docker en un VPS Ubuntu cubre el motor, Compose y el comportamiento en el arranque. Su advertencia sobre ufw no afecta a un bot, que no publica ningún puerto.
Logs, actualizaciones y dimensionado de la memoria
Logs
journalctl -u mybot -f # follow live
journalctl -u mybot --since "1 hour ago"
journalctl -u mybot -p err -b # errors since boot
sudo journalctl --vacuum-size=200M # cap journal size
journald rota y comprime por su cuenta. Si el bot escribe su propio archivo de log, usa logging.handlers.RotatingFileHandler en Python o un transporte rotativo con pino o winston en Node, para que un handler demasiado hablador no llene el disco.
Actualizaciones
cd /opt/mybot
sudo -u bot git pull
sudo -u bot .venv/bin/pip install -U -r requirements.txt # Node: sudo -u bot npm ci
sudo systemctl restart mybot
Un reinicio corta la conexión con la gateway durante unos segundos; la biblioteca vuelve a conectarse y los usuarios rara vez lo notan. Fija las versiones de las bibliotecas en requirements.txt o package-lock.json y lee el changelog antes de un salto de versión mayor. Mantén el sistema al día con sudo apt update && sudo apt upgrade o con unattended-upgrades.
¿Cuánta RAM necesita el bot?
No dimensiones a partir de cifras genéricas; mide. systemctl status mybot imprime una línea Memory: y pm2 list muestra lo mismo. Un bot de discord.py solo de comandos suele quedarse en decenas de megabytes en reposo; un bot de discord.js carga además el runtime de Node. La memoria crece con el estado: cachés de miembros y mensajes en servidores grandes, voz y música (un proceso ffmpeg por stream), generación de imágenes o una base de datos. En la práctica, 1 GB de RAM hace funcionar con holgura el sistema más un bot pequeño o mediano; los bots con mucha voz o varios bots en una misma máquina justifican 2 GB o más, y por encima de 2.500 servidores Discord exige sharding, lo que multiplica el consumo. Dos redes de seguridad baratas: MemoryMax=512M en la unidad, para que una fuga reinicie el bot en vez de tumbar la máquina, y un pequeño archivo de swap.
Todo esto presupone una máquina Linux con acceso root que se mantiene activa a todas horas. Si aún te falta esa máquina, el VPS para bots de Discord de rdp.monster está en línea unos 10 segundos después de la confirmación del pago, con CPU y RAM dedicadas, acceso root completo, ancho de banda ilimitado (fair-use), sin KYC, con criptomonedas aceptadas, desde $8.99/mes, para que el systemctl enable --now mybot de más arriba sea lo último que escribas antes de ver el bot en verde.
Preguntas frecuentes
¿Puedo alojar un bot de Discord gratis 24/7?
systemd reinicia el bot tras un fallo o un reinicio y nadie lo suspende por estar inactivo.¿Necesita un bot de Discord una IP fija o puertos abiertos en el VPS?
¿Puedo ejecutar varios bots de Discord en un mismo VPS?
/opt/bot-a y /opt/bot-b, su propio .env con su propio token y su propio archivo de unidad, y luego ejecuta systemctl enable --now en cada uno. Usuarios de sistema separados impiden que un fallo en un bot lea el token del otro. El único presupuesto compartido es la RAM y la CPU: vigila systemctl status en cada unidad y pasa a un plan mayor cuando el total se acerque al límite.¿Debo alojar un bot de Discord en un VPS Windows o Linux?
systemd de diez líneas. En Windows usarías el Programador de tareas o NSSM para lograr el mismo reinicio automático con más clics.Adrien Roche, Editor de infraestructura y hosting
Ingeniero de sistemas con más de 10 años operando flotas de Windows Server y Linux. Adrien mantiene la documentación de infraestructura de rdp.monster y escribe nuestras guías sobre RDP, hosting VPS, administración de servidores, redes y herramientas de privacidad.




