---
title: "How I deploy my ~4,000 MAU Python Telegram bot"
description: "Using semantic release, Docker and watchtower to deploy my Python Telegram bot."
datePublished: 2022-09-24T14:30:00.000Z
slug: "deploying-my-python-telegram-bot"
previewImage: "/preview/deploying-python-telegram-bot.jpg"
permalink: "/blog/deploying-my-python-telegram-bot"
---
import Image from "../../components/OptimizedImage.astro";
import Logo from "../../assets/images/ssgbot-logo.png";

## Introduction

<div class='flex flex-row flex-wrap items-start md:flex-nowrap' >
  <Image src={ Logo } alt = 'SuperSeriousBot logo' class="mr-10 w-36 m-auto" />

  <p>
    I often get asked about what the “best” way to deploy a Python Telegram bot is. I’ve tried a lot of different ways, and I’ve settled on a workflow that I’m quite happy with for <a href = "https://github.com/obviyus/SuperSeriousBot" ><code>@SuperSeriousBot</code></a>. It’s probably not ideal and I’d like to change a few things, but it’s a process that has evolved over the last ~4 years.
  </p>
</div>

## Committing

In general I like to follow [KISS](https://en.wikipedia.org/wiki/KISS_principle) for my projects. The actual code of `@SuperSeriousBot` is fairly straightforward. In a nutshell:

- Python 3.10 (asyncio)
- [Poetry](https://python-poetry.org/) for dependency management
- [`black`](https://github.com/psf/black) for code formatting
- The fantastic [`python-telegram-bot`](https://github.com/python-telegram-bot/python-telegram-bot) wrapper as the core

The code is stored on GitHub. For releases, I follow the [Angular commit message convention](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format). Using this, I'm able to largely automate the process of version management by using [`semantic-release`](https://github.com/semantic-release/semantic-release).

## Building

Being on GitHub, I make heavy use of GitHub actions. I have a workflow that runs on every push to the `master` branch. This workflow does the following:

- Run `npx semantic-release` to calculate the next version number
- Publish a release on the repository page with an auto-generated changelog
- Build Docker images of `@SuperSeriousBot` for `amd64` and `arm64` architectures
- Push the Docker images to [ghcr.io](https://github.com/features/packages)

I've tried to trim down the Docker image size but it still sits at around ~400MB. This part's still a work in progress. Lately, I've also been looking into [Nuitka](https://nuitka.net/) for static binaries. No luck on that front so far. All builds are cached based on `poetry.lock` so we never rebuild layers. It matters a lot when you're building for multiple architectures.

As of right now, there's no tests. It's up for discussion if I'll ever add any. To me, a good code review process and robust logging is sufficient for a side project.

Here's the `.releaserc` I use to automate the version management and changelog generation:

```json
{
	"branches": ["master"],
	"plugins": [
		"@semantic-release/commit-analyzer",
		"@semantic-release/release-notes-generator",
		"@semantic-release/changelog",
		"@semantic-release/git",
		"@semantic-release/github"
	]
}
```

## Deploying

I use [watchtower](https://containrrr.dev/watchtower/) to automatically update my Docker containers. I have a `docker-compose.yml` file that looks like this:

```yaml
version: "3"
services:
  ssgbot: ...

  redis: ...

  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 30
```

It's not a zero downtime deployment, so I usually like to do it in the late AMs. Along with the bot itself, I use a mix of Redis and SQLite for caching and persistence. In production, I run `@SuperSeriousBot` in [webhook mode](https://core.telegram.org/bots/api#setwebhook). I front the bot with [nginx](https://www.nginx.com/) for which I use `systemd`.

## Conclusion

I find this setup to be quite robust and easy to maintain. If you have any suggestions, feel free to reach out to me on Telegram at [@obviyus](https://obviyus.t.me/).

## Wishlist

- [ ] Use Hashicorp Nomad for container orchestration
- [ ] Experiment with Nuitka for static binaries
- [ ] DuckDB instead of SQLite