Documentation Index
Fetch the complete documentation index at: https://mintlify.com/grammyjs/grammY/llms.txt
Use this file to discover all available pages before exploring further.
This guide covers deploying grammY bots to various platforms and environments. Choose the platform that best fits your needs.
Traditional Servers
VPS, dedicated servers, or containers. Full control, always running.
Serverless
Function-as-a-Service platforms. Pay per use, automatic scaling.
PaaS
Platform-as-a-Service like Heroku or Railway. Easy deployment.
Edge Computing
Cloudflare Workers, Deno Deploy. Global distribution.
Deployment Checklist
Before deploying:
Traditional Server Deployment
Using PM2 (Node.js)
# Install PM2
npm install -g pm2
# Start your bot
pm2 start bot.js --name "my-bot"
# Save PM2 configuration
pm2 save
# Setup PM2 to start on boot
pm2 startup
ecosystem.config.js for PM2:
module.exports = {
apps: [{
name: "telegram-bot",
script: "./dist/bot.js",
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: "1G",
env: {
NODE_ENV: "production",
},
}],
};
Using systemd (Linux)
/etc/systemd/system/telegram-bot.service:
[Unit]
Description=Telegram Bot
After=network.target
[Service]
Type=simple
User=botuser
WorkingDirectory=/home/botuser/bot
Environment="NODE_ENV=production"
EnvironmentFile=/home/botuser/bot/.env
ExecStart=/usr/bin/node /home/botuser/bot/dist/bot.js
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable telegram-bot
sudo systemctl start telegram-bot
sudo systemctl status telegram-bot
Docker Deployment
Dockerfile:
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source
COPY . .
# Build if using TypeScript
RUN npm run build
# Start bot
CMD ["node", "dist/bot.js"]
docker-compose.yml:
version: '3.8'
services:
bot:
build: .
environment:
- BOT_TOKEN=${BOT_TOKEN}
- NODE_ENV=production
restart: unless-stopped
volumes:
- ./data:/app/data
Deploy:
Cloudflare Workers
import { Bot, webhookCallback } from "grammy";
const bot = new Bot("YOUR_BOT_TOKEN");
bot.on("message", (ctx) => ctx.reply("Hello from Cloudflare!"));
export default {
async fetch(request: Request, env: any) {
if (request.method === "POST") {
return await webhookCallback(bot, "cloudflare")(request);
}
return new Response("OK");
},
};
wrangler.toml:
name = "telegram-bot"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[vars]
BOT_TOKEN = "your_token_here"
Deploy:
Deno Deploy
import { Bot, webhookCallback } from "https://deno.land/x/grammy/mod.ts";
const bot = new Bot(Deno.env.get("BOT_TOKEN")!);
bot.on("message", (ctx) => ctx.reply("Hello from Deno!"));
const handleUpdate = webhookCallback(bot, "std/http");
Deno.serve(async (req) => {
const url = new URL(req.url);
if (url.pathname === "/webhook" && req.method === "POST") {
return await handleUpdate(req);
}
return new Response("Not Found", { status: 404 });
});
Deploy:
deployctl deploy --project=my-bot --entrypoint=bot.ts
Vercel
api/webhook.ts:
import { Bot, webhookCallback } from "grammy";
const bot = new Bot(process.env.BOT_TOKEN!);
bot.on("message", (ctx) => ctx.reply("Hello from Vercel!"));
export default webhookCallback(bot, "std/http");
vercel.json:
{
"functions": {
"api/webhook.ts": {
"memory": 1024,
"maxDuration": 10
}
}
}
Deploy:
AWS Lambda
import { Bot, webhookCallback } from "grammy";
import { APIGatewayProxyHandler } from "aws-lambda";
const bot = new Bot(process.env.BOT_TOKEN!);
bot.on("message", (ctx) => ctx.reply("Hello from Lambda!"));
const handleUpdate = webhookCallback(bot, "aws-lambda");
export const handler: APIGatewayProxyHandler = async (event, context) => {
return await handleUpdate(event, context);
};
Railway
- Connect your GitHub repository
- Add environment variables (BOT_TOKEN)
- Railway auto-detects Node.js and deploys
- For webhooks, use the provided domain
Heroku
Procfile:
package.json (add):
{
"engines": {
"node": "20.x"
}
}
Deploy:
heroku login
heroku create my-telegram-bot
heroku config:set BOT_TOKEN=your_token
git push heroku main
Render
- Connect GitHub repository
- Select “Web Service” for webhooks or “Background Worker” for polling
- Set build command:
npm install && npm run build
- Set start command:
node dist/bot.js
- Add BOT_TOKEN environment variable
Environment Variables
.env.example:
BOT_TOKEN=your_bot_token_here
WEBHOOK_URL=https://your-domain.com/webhook
WEBHOOK_SECRET=your_secret_token
NODE_ENV=production
PORT=8080
Loading environment variables:
import { config } from "dotenv";
// Load .env file in development
if (process.env.NODE_ENV !== "production") {
config();
}
const BOT_TOKEN = process.env.BOT_TOKEN;
if (!BOT_TOKEN) {
throw new Error("BOT_TOKEN is not set");
}
Production Configuration
import { Bot } from "grammy";
const bot = new Bot(process.env.BOT_TOKEN!);
// Production error handling
bot.catch((err) => {
console.error("Bot error:", err);
// Send to error tracking service (Sentry, etc.)
});
// Choose deployment method based on environment
if (process.env.USE_WEBHOOKS === "true") {
// Webhook setup
const webhookUrl = process.env.WEBHOOK_URL!;
await bot.api.setWebhook(webhookUrl, {
secret_token: process.env.WEBHOOK_SECRET,
drop_pending_updates: true,
});
// Start web server
// (Express/Fastify/etc. code)
} else {
// Long polling
bot.start({
drop_pending_updates: true,
onStart: ({ username }) => {
console.log(`Bot @${username} started`);
},
});
}
// Graceful shutdown
process.once("SIGINT", () => bot.stop("SIGINT"));
process.once("SIGTERM", () => bot.stop("SIGTERM"));
Monitoring
Health Check Endpoint
app.get("/health", (req, res) => {
res.json({
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
});
});
Error Tracking (Sentry)
import * as Sentry from "@sentry/node";
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
});
bot.catch((err) => {
console.error("Bot error:", err);
Sentry.captureException(err.error);
});
Security Best Practices
Protect Your Token
Never commit tokens to git. Use environment variables and secrets management.
Validate Webhooks
Use secret tokens to verify webhook requests are from Telegram.
Rate Limiting
Implement rate limiting to prevent abuse.
HTTPS Only
Use HTTPS for webhooks. Telegram requires valid SSL certificates.
- Use webhooks instead of polling in production
- Enable concurrent update processing when appropriate
- Implement caching for frequently accessed data
- Use database connection pooling
- Monitor and optimize slow handlers
- Set appropriate
max_connections for webhooks
Scaling
Horizontal Scaling
For high-traffic bots:
- Use webhooks with a load balancer
- Run multiple instances behind the load balancer
- Use a shared database/cache (Redis, PostgreSQL)
- Ensure stateless bot handlers
Database
For persistent storage:
import { Bot, session } from "grammy";
import { MongoDBAdapter } from "@grammyjs/storage-mongodb";
import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();
const db = client.db("telegram-bot");
const storage = new MongoDBAdapter({ collection: db.collection("sessions") });
bot.use(session({
initial: () => ({}),
storage,
}));
See Also