Match the runtime to the delivery model
| Bot type | Needs | Good fits |
|---|---|---|
| Webhook-only (Telegram webhook, Discord HTTP interactions, Slack Events, WhatsApp) | An HTTPS endpoint | Serverless functions, edge workers, any web host |
Long polling (Telegram getUpdates) |
Always-on process | VPS, container platform, PaaS worker |
| Gateway or Socket Mode (Discord Gateway, Slack Socket Mode) | Always-on process with a WebSocket | VPS, container platform, PaaS worker |
| Heavy AI or media processing | CPU or GPU, memory | VPS, container platform, a separate job queue |
| Scheduled jobs | A scheduler | Cron Triggers, cloud schedulers, GitHub Actions, systemd timers |
Options in detail
Serverless and edge (Cloudflare Workers, AWS Lambda, Google Cloud Run, Deno Deploy). You pay per request, it scales to zero, and TLS is included. Limits on execution time and no persistent sockets mean you design around webhooks, queues and external state. Excellent for most chat bots. See Cloudflare Workers for bots.
Platform as a service (Render, Railway, Fly.io and similar). Push a repository or a container and get a running service, background workers, logs and managed databases. A good middle ground for Gateway bots.
VPS (Hetzner, DigitalOcean, OVHcloud, Linode and others). Full control and predictable cost from a few dollars a month. You are responsible for updates, firewall, TLS, process supervision and backups.
Your own computer is fine for development. For production it loses power, network and sleep battles; use a tunnel only for testing.
Running on a VPS with systemd
# /etc/systemd/system/mybot.service
[Unit]
Description=My Discord bot
After=network-online.target
Wants=network-online.target
[Service]
User=mybot
WorkingDirectory=/opt/mybot
EnvironmentFile=/etc/mybot.env
ExecStart=/usr/bin/node dist/index.js
Restart=always
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/mybot/data
[Install]
WantedBy=multi-user.target
sudo useradd --system --home /opt/mybot mybot
sudo chmod 600 /etc/mybot.env
sudo systemctl daemon-reload && sudo systemctl enable --now mybot
journalctl -u mybot -f
The bot runs as an unprivileged user, restarts on crash, reads secrets from a root-owned file, and has a read-only view of the system.
Running with Docker Compose
services:
bot:
build: .
restart: unless-stopped
env_file: .env # never commit this file
read_only: true
tmpfs: [/tmp]
depends_on: [redis]
healthcheck:
test: ["CMD", "node", "healthcheck.js"]
interval: 30s
retries: 3
redis:
image: redis:7-alpine
restart: unless-stopped
volumes: [redis-data:/data]
volumes:
redis-data:
Use a small base image, run as a non-root user in the Dockerfile, and pin dependency versions with a lockfile.
Webhooks on a VPS
Put a reverse proxy (Caddy, nginx) in front of your app for TLS, or use a tunnel such as Cloudflare Tunnel so you do not open inbound ports at all. Only expose the webhook paths; keep admin dashboards on localhost or behind authentication.
Operations checklist
- Health: a
/healthzendpoint for webhook services, or a heartbeat for workers that an external monitor checks. - Logs: structured JSON to stdout, collected by journald, the platform or a log service. Redact tokens and personal data.
- Alerts: on crash loops, error rate, queue depth and webhook silence (no events for an unusual period).
- Backups: databases and any local state, with a tested restore.
- Updates: automatic OS security updates, regular dependency updates, and a reproducible deploy.
- Single instance: only one process should poll
getUpdatesor hold a Gateway session per shard. Two copies cause conflicts and duplicate replies.
Cost tips
A typical community bot with webhooks costs nothing on free serverless tiers. Gateway bots fit in a few hundred megabytes of RAM on a small VPS. The expensive parts are usually AI model calls and media processing: measure them separately and put quotas in place before you go viral.