grammY + Docker + GitHub Actions CI/CD + one-click deploy.
Build your bot. Push to deploy.
English | 한국어
Part of Starter Series — Stop explaining CI/CD to your AI every time. Clone and start.
Docker Deploy · Discord Bot · Telegram Bot · Browser Extension · Electron App · npm Package · React Native · VS Code Extension · MCP Server · Python MCP Server · Cloudflare Pages
Via create-starter (recommended):
npx @starter-series/create my-telegram-bot --template telegram-bot
cd my-telegram-bot && npm install
cp .env.example .env # fill in BOT_TOKEN from @BotFather
npm run devOr clone directly:
git clone https://github.com/starter-series/telegram-bot-starter my-telegram-bot
cd my-telegram-bot && npm install
cp .env.example .env
npm run devSee docs/TELEGRAM_SETUP.md for the detailed BotFather guide.
├── src/
│ ├── index.js # Entry point (polling or webhook)
│ ├── config.js # Environment config loader
│ ├── commands/ # Bot commands (auto-loaded)
│ │ ├── start.js # /start — greeting
│ │ └── help.js # /help — list commands
│ ├── handlers/ # Message handlers (auto-loaded)
│ │ └── echo.js # Echo text messages
│ └── lib/
│ ├── health.js # GET /health HTTP server
│ ├── logger.js # Structured JSON logger
│ └── rate-limiter.js # Per-user rate limiter
├── scripts/
│ └── bump-version.js # Bump package.json version
├── tests/
│ ├── commands.test.js # Command + handler tests
│ └── health.test.js # Health endpoint tests
├── Dockerfile # Production container
├── docker-compose.yml # Dev with hot reload
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # Audit, lint, test, Docker build
│ │ ├── cd-railway.yml # Deploy to Railway
│ │ ├── cd-fly.yml # Deploy to Fly.io
│ │ └── setup.yml # Auto setup checklist on first use
│ └── PULL_REQUEST_TEMPLATE.md
├── docs/
│ ├── TELEGRAM_SETUP.md # BotFather setup guide
│ └── DEPLOY_GUIDE.md # Railway & Fly.io deployment guide
└── package.json
- grammY — Modern, fast, lightweight Telegram bot framework
- CI Pipeline — Security audit, lint, test, Docker build verification on every push
- CD Pipeline — One-click deploy to Railway or Fly.io + auto GitHub Release
- Docker — Production Dockerfile + dev compose with hot reload
- Polling & Webhook — Long polling by default, webhook mode via env var
- Health endpoint — Built-in
GET /health+ DockerHEALTHCHECKso Fly.io / Railway can detect a crashed bot - Version management —
npm run version:patch/minor/majorto bumppackage.json - Dev mode —
npm run devfor live reload withnode --watch - Starter code —
/start+/helpcommands, echo handler, modular structure - Deploy guides — Step-by-step docs for BotFather, Railway, and Fly.io
- Template setup — Auto-creates setup checklist issue on first use
| Step | What it does |
|---|---|
| Security audit | npm audit for dependency vulnerabilities |
| Lint | ESLint for code quality |
| Test | Jest (passes with no tests by default) |
| Docker build | Builds the container image to catch build errors |
| Trivy scan | Scans the container image for CRITICAL/HIGH CVEs |
| Workflow | What it does |
|---|---|
CodeQL (codeql.yml) |
Static analysis for security vulnerabilities (push/PR + weekly) |
Maintenance (maintenance.yml) |
Weekly CI health check — auto-creates issue on failure |
Stale (stale.yml) |
Labels inactive issues/PRs after 30 days, auto-closes after 7 more |
| Step | What it does |
|---|---|
| Version guard | Fails if git tag already exists for this version |
| Deploy | Pushes to Railway or Fly.io |
| GitHub Release | Creates a tagged release with auto-generated notes |
How to deploy:
- Set up GitHub Secrets (see below)
- Bump version:
npm run version:patch(orversion:minor/version:major) - Go to Actions tab → Deploy to Railway (or Fly.io) → Run workflow
| Secret | Description |
|---|---|
RAILWAY_TOKEN |
Railway API token |
RAILWAY_SERVICE_ID |
Target service ID |
See docs/DEPLOY_GUIDE.md for setup guide.
| Secret | Description |
|---|---|
FLY_API_TOKEN |
Fly.io deploy token |
See docs/DEPLOY_GUIDE.md for setup guide.
# Start with hot reload
npm run dev
# Or use Docker
docker compose up
# Bump version (updates package.json)
npm run version:patch # 1.0.0 → 1.0.1
npm run version:minor # 1.0.0 → 1.1.0
npm run version:major # 1.0.0 → 2.0.0
# Lint & test
npm run lint
npm testCreate a new file in src/commands/:
// src/commands/echo.js
module.exports = {
name: 'echo',
description: 'Echo your message',
async execute(ctx) {
const text = ctx.match || 'Nothing to echo';
await ctx.reply(text);
},
};Commands are auto-loaded — no need to edit any other file.
Create a new file in src/handlers/:
// src/handlers/photo.js
module.exports = {
name: 'photo',
register(bot) {
bot.on('message:photo', async (ctx) => {
await ctx.reply('Nice photo!');
});
},
};Handlers are auto-loaded — no need to edit any other file.
donbarbos/telegram-bot-template (450+ stars) is the most complete Telegram bot template. This template takes a different approach:
| This template | donbarbos/telegram-bot-template | |
|---|---|---|
| Philosophy | Thin starter with CI/CD | Full production stack |
| Framework | grammY (JS) | aiogram (Python) |
| Infrastructure | Docker only | Docker + PostgreSQL + Redis + Grafana |
| Dependencies | 2 runtime | 15+ |
| Learning curve | Read the grammY docs | Learn the full stack |
| CI/CD | Full pipeline included | Full pipeline included |
| AI/vibe-coding | LLMs generate clean JS | LLMs must handle Python + ORM + monitoring |
| Best for | Utility bots, simple automation | Large bots with DB, caching, monitoring |
Choose this template if:
- You want a lightweight bot that's easy to understand and extend
- You need production CI/CD + Docker without the heavyweight stack
- You're using AI tools to generate bot code — simple JS produces the cleanest output
- Your bot doesn't need a database or Redis
Choose donbarbos if:
- You need PostgreSQL, Redis, or monitoring from day one
- You prefer Python/aiogram over JavaScript/grammY
- You want a full production stack out of the box
This template uses JavaScript for simplicity. To add TypeScript:
- Add
typescriptand@types/nodeto devDependencies - Add a
tsconfig.json - Update
npm startto build and run fromdist/ - Rename
.jsfiles to.ts
TypeScript is opt-in, not forced. For many bots, JavaScript is all you need.
The bot exposes a tiny HTTP health server (src/lib/health.js) so Docker, Fly.io, and Railway can detect a crashed or stuck bot process.
| Mode | Port | Endpoint |
|---|---|---|
| Polling (default) | HEALTH_PORT (default 3000) — standalone server |
GET /health |
Webhook (WEBHOOK_URL set) |
PORT — mounted on the webhook server, no extra listener |
GET /health |
| Status | Body |
|---|---|
200 OK (ready) |
{ "status": "ok", "uptime": <seconds>, "mode": "polling"|"webhook" } |
503 Service Unavailable (starting / disconnected) |
{ "status": "starting", "uptime": <seconds>, "mode": "polling"|"webhook" } |
Configuration
# .env
HEALTH_PORT=3000 # polling mode only — change if 3000 is already taken
# PORT=3000 # webhook mode — /health is mounted on this portFly.io — add an HTTP service check to fly.toml:
[[http_service]]
internal_port = 3000
force_https = true
[[http_service.checks]]
interval = "30s"
timeout = "5s"
grace_period = "30s"
method = "GET"
path = "/health"Railway — in the service's Settings → Deploy, set the health-check path to /health and the port to 3000.
Docker — docker ps will show (healthy) / (unhealthy) automatically; the HEALTHCHECK runs wget --spider http://localhost:${HEALTH_PORT}/health every 30s.
PRs welcome. Please use the PR template.