Hosting: Fly
This guide tells you about the ways you can host your grammY bots on Fly, either using Deno or Node.js.
Preparing Your Code
You can run your bot using both webhooks or long polling.
Webhooks
Remember that you should not call
bot
in your code when using webhooks..start()
- Make sure that you have a file which exports your
Bot
object, so that you can import it later to run it. - Create a file named
app
or.ts app
, or actually any name you like (but you should be remembering and using this as the main file to deploy), with the following content:.js
import { serve } from "https://deno.land/std@0.177.1/http/server.ts";
import { webhookCallback } from "https://deno.land/x/grammy@v1.16.1/mod.ts";
// You might modify this to the correct way to import your `Bot` object.
import { bot } from "./bot.ts";
const port = 8000;
const handleUpdate = webhookCallback(bot, "std/http");
serve(async (req) => {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname.slice(1) === bot.token) {
try {
return await handleUpdate(req);
} catch (err) {
console.error(err);
}
}
return new Response();
}, { port });
import express from "express";
import { webhookCallback } from "grammy";
// You might modify this to the correct way to import your `Bot` object.
import { bot } from "./bot";
const port = 8000;
const app = express();
app.use(express.json());
app.use(`/${bot.token}`, webhookCallback(bot, "express"));
app.use((_req, res) => res.status(200).send());
app.listen(port, () => console.log(`listening on port ${port}`));
We advise you to have your handler on some secret path rather than the root (/
). As shown in the highlighted line above, we are using the bot token (/<bot token>
) as the secret path.
Long Polling
Create a file named app
or app
, or actually any name you like (but you should be remembering and using this as the main file to deploy), with the following content:
import { Bot } from "https://deno.land/x/grammy@v1.16.1/mod.ts";
const token = Deno.env.get("BOT_TOKEN");
if (!token) throw new Error("BOT_TOKEN is unset");
const bot = new Bot(token);
bot.command(
"start",
(ctx) => ctx.reply("I'm running on Fly using long polling!"),
);
Deno.addSignalListener("SIGINT", () => bot.stop());
Deno.addSignalListener("SIGTERM", () => bot.stop());
bot.start();
import { Bot } from "grammy";
const token = process.env.BOT_TOKEN;
if (!token) throw new Error("BOT_TOKEN is unset");
const bot = new Bot(token);
bot.command(
"start",
(ctx) => ctx.reply("I'm running on Fly using long polling!"),
);
process.once("SIGINT", () => bot.stop());
process.once("SIGTERM", () => bot.stop());
bot.start();
As you can see in the highlighted line above, we take some sensitive values (your bot token) from environment variables. Fly allow us to store that secret by running this command:
flyctl secrets set BOT_TOKEN="AAAA:12345"
You can specify other secrets in the same way. For more information about this secrets, see https://
Deploying
flyctl
Method 1: With This is the easiest method to go with.
- Install flyctl and sign in.
- Run
flyctl launch
to generate aDockerfile
andfly
file for deployment. But DO NOT deploy..toml
flyctl launch
Creating app in /my/telegram/bot
Scanning source code
Detected a Deno app
? App Name (leave blank to use an auto-generated name): grammy
Automatically selected personal organization: CatDestroyer
? Select region: ams (Amsterdam, Netherlands)
Created app grammy in organization personal
Wrote config file fly.toml
? Would you like to set up a Postgresql database now? No
? Would you like to deploy now? No
Your app is ready. Deploy with `flyctl deploy`
flyctl launch
Creating app in /my/telegram/bot
Scanning source code
Detected a NodeJS app
Using the following build configuration:
Builder: heroku/buildpacks:20
? App Name (leave blank to use an auto-generated name): grammy
Automatically selected personal organization: CatDestroyer
? Select region: ams (Amsterdam, Netherlands)
Created app grammy in organization personal
Wrote config file fly.toml
? Would you like to set up a Postgresql database now? No
? Would you like to deploy now? No
Your app is ready. Deploy with `flyctl deploy`
Deno: Change the Deno version and remove
CMD
if exist within theDockerfile
file. For example, in this case, we updateDENO
to_VERSION 1
..25 .2 Node.js: To change the Node.js version, you need to insert a
"node"
property inside an"engines"
property insidepackage
. For instance, we update the Node.js version to.json 16
in the example below..14 .0
# Dockerfile
ARG DENO_VERSION=1.25.2
ARG BIN_IMAGE=denoland/deno:bin-${DENO_VERSION}
FROM ${BIN_IMAGE} AS bin
FROM frolvlad/alpine-glibc:alpine-3.13
RUN apk --no-cache add ca-certificates
RUN addgroup --gid 1000 deno \
&& adduser --uid 1000 --disabled-password deno --ingroup deno \
&& mkdir /deno-dir/ \
&& chown deno:deno /deno-dir/
ENV DENO_DIR /deno-dir/
ENV DENO_INSTALL_ROOT /usr/local
ARG DENO_VERSION
ENV DENO_VERSION=${DENO_VERSION}
COPY /deno /bin/deno
WORKDIR /deno-dir
COPY . .
ENTRYPOINT ["/bin/deno"]
# CMD is removed
// package.json
{
"name": "grammy",
"version": "1.0.0",
"description": "grammy",
"main": "app.js",
"author": "itsmeMario",
"license": "MIT",
"dependencies": {
"express": "^4.18.1",
"grammy": "^1.11.0"
},
"devDependencies": {
"@types/express": "^4.17.14",
"@types/node": "^18.7.18",
"typescript": "^4.8.3"
},
"engines": {
"node": "16.14.0"
}
}
- Edit
app
inside thefly
file. The path.toml .
(or/app .ts .
for Node.js) in the example below refers to the main file directory. You might modify them to match with your project’s directory. If you are using webhooks, make sure the port is same as the one in your configuration (/app .js 8000
).
# fly.toml
app = "grammy"
kill_signal = "SIGINT"
kill_timeout = 5
[processes]
app = "run --allow-net ./app.ts"
[[services]]
http_checks = []
internal_port = 8000
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"
# fly.toml
app = "grammy"
kill_signal = "SIGINT"
kill_timeout = 5
[processes]
app = "run --allow-net ./app.ts"
# Simply omitting the whole [[services]] section
# since we are not listening to HTTP
# fly.toml
app = "grammy"
kill_signal = "SIGINT"
kill_timeout = 5
[processes]
app = "node ./build/app.js"
# Adjust the NODE_ENV environment variable to suppress the warning
[build.args]
NODE_ENV = "production"
[build]
builder = "heroku/buildpacks:20"
[[services]]
http_checks = []
internal_port = 8000
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"
# fly.toml
app = "grammy"
kill_signal = "SIGINT"
kill_timeout = 5
[processes]
app = "node ./build/app.js"
# Adjust the NODE_ENV environment variable to suppress the warning
[build.args]
NODE_ENV = "production"
[build]
builder = "heroku/buildpacks:20"
# Simply omitting the whole of the [[services]] section since we are not listening to HTTP.
- Run
flyctl deploy
to deploy your code.
Method 2: With GitHub Actions
The main advantage of following method is that Fly will watch for changes in your repository which includes your bot code, and it will deploy new versions automatically. Visit https://
- Install flyctl and sign in.
- Get a Fly API token by running
flyctl auth token
. - Create a repository on GitHub, it can be either private or public.
- Go to Settings, choose Secrets and create a secret called
FLY
with the value of the token from step 2._API _TOKEN - Create
.github
with these contents:/workflows /main .yml
name: Fly Deploy
on: [push]
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
- Follow steps 2 until 4 from Method 1 above. Remember to skip the last step (step 5) since we are not deploying the code directly.
- Commit your changes and push them up to GitHub.
- This is where the magic happens—the push will have triggered a deploy and from now on, whenever you push a change, the app will automatically be redeployed.
Setting the Webhook URL
If you are using webhooks, after getting your app running, you should configure your bot’s webhook settings to point to your app. To do that, send a request to
https://api.telegram.org/bot<token>/setWebhook?url=<url>
replacing <token>
with your bot’s token, and <url>
with the full URL of your app along with the path to the webhook handler.
Dockerfile Optimization
When our Dockerfile
is run, it copies everything from the directory over to the Docker image. For Node.js applications, some directories like node
are going to be rebuilt anyway so there’s no need to copy them. Create a .dockerignore
file and add node
to it to do this. You can also use .dockerignore
to not copy any other files which aren’t needed at runtime.