Skip to content

Installation

pip install 'django-aiogram[redis]'

The default: no BROKER needed.

pip install 'django-aiogram[redis]'

Same driver as the list — a different data structure on the server, not a different dependency.

pip install 'django-aiogram[rabbitmq,redis]'

redis for the FSM store, which is aiogram's and defaults on; see below.

pip install 'django-aiogram[kafka,redis]'

redis for the FSM store, which is aiogram's and defaults on; see below.

Requires Python 3.10–3.14, Django 5.2+ and aiogram 3.30+. Every pair in that range runs in CI, and one leg pins the floors themselves — django==5.2.0 aiogram==3.30.0 redis==6.2.0 on Python 3.10 — because a floor nothing installs is a floor nobody has tried.

Then the transport you name adds two of its own, and they are different questions: the driver is a Python package this project pins, and the server is what you run.

transport driver server
Redis list redis>=6.2 any, but BLMOVE — Redis 6.2 — is what makes delivery at-least-once. Below it the consumer says so in the log and drops to at-most-once rather than refusing
Redis Streams redis>=6.2 7.0 or newer, enforced. depth() reads lag from XINFO GROUPS, a field that arrived in 7.0, and the transport refuses rather than reporting a number a healthcheck would act on
RabbitMQ pika>=1.3 RabbitMQ 4 in CI; 1.3.0 of the driver runs the whole integration suite against it
Kafka confluent-kafka>=2.6 below Python 3.14, confluent-kafka>=2.12.1 on Python 3.14 and newer Apache Kafka 4 in CI

One extra is not always enough, and the default is why. FSM_STORAGE defaults to 'redis', and that store is aiogram's — it imports redis-py. So a project on RabbitMQ or Kafka that installs only its own transport's extra has a bot that starts and a dispatcher that cannot be built:

pip install 'django-aiogram[kafka,redis]'   # Kafka for the queue, redis for the FSM store

or keep one extra and say the state lives in the process:

TELEGRAM_BOT_DEFAULTS = {'FSM_STORAGE': 'memory', ...}   # per process, lost on restart

manage.py check reports E019 for exactly one configuration — FSM_STORAGE naming the Redis store on an install that has no redis-py — and says nothing once either line above is in place. It used to say nothing at all: start_tgbot died on ModuleNotFoundError: No module named 'redis' while building the dispatcher. A missing driver reaching a project as a traceback is the shape E047 prevents for the broker; E019 is the rule for this one.

The Kafka floor is two numbers because that driver compiles librdkafka: the first wheel for Python 3.13 is 2.6.0 and the first for 3.14 is 2.12.1, so an older pin does not fail to work there — it fails to install, asking for a C header. The API needs nothing newer than the wheels do, measured: 2.6.0 passes every Kafka case against a real broker.

The transport driver is an extra, so a deployment downloads the one it uses and not the other three. BROKER names the transport and nothing is inferred from what happens to be installed, so the two have to agree; when they do not, manage.py check says so with the install line for the one you named:

?: (django_aiogram.E047) TELEGRAM_BOT_DEFAULTS['BROKER'] names
   'django_aiogram.broker.redis_list.RedisListBroker', whose driver is not installed.
    HINT: pip install "django-aiogram[redis]"

Four extras are not transports. hiredis swaps redis-py's parser for the C one, prometheus (prometheus-client>=0.20) installs the client the shipped exporter fills, crypto (cryptography>=42) the encrypting TOKEN_STORAGE, and webhook (uvicorn>=0.30) the server start_tgbot --serve runs:

pip install 'django-aiogram[redis,prometheus,crypto]'

crypto is needed only where TOKEN_STORAGE names the encrypting storage; a deployment whose database is already the trust boundary keeps its tokens as they are and never imports cryptography. See Tokens.

webhook is needed only by a deployment that receives its updates in the bot container rather than in its web tier: serving the view from the project's own ASGI server needs nothing extra at all. start_tgbot --serve refuses to start without it, naming every package of the group that is missing. See Webhook.

django_aiogram.contrib.prometheus is the only module that imports prometheus_client, and nothing in this package imports that module — a project does, from its own AppConfig.ready(). So without the extra the failure is an ImportError on the line that asked for it, rather than a broken install for everyone who never wanted metrics. See Event log.

A base pip install django-aiogram is a valid install — it imports, and every manage.py command runs — it just cannot carry a message anywhere yet. A process with ENABLED off is not asked for a driver, so a web container that only records the event log needs no extra. A process that reads the queue depth does need one even when disabled: those reads are not gated on ENABLED, and manage.py check cannot tell which processes make them.

The redis driver floor is 6.2 because aiogram's RedisStorage asks for exactly that — redis[hiredis]>=6.2.0 in its own metadata — and FSM_STORAGE: 'redis' is the default, so every transport can reach the storage even when the queue itself is somewhere else. On redis-py below 5.0.1 the storage raises AttributeError: 'Redis' object has no attribute 'aclose'. redis-py 8 is tested here and works, though aiogram's own optional extra stops at 7.

Add the app

# settings.py
import os

INSTALLED_APPS = [
    ...,
    'django_aiogram',
]

TELEGRAM_BOT_DEFAULTS = {
    'TOKEN': os.environ.get('TELEGRAM_BOT_TOKEN', ''),
    'REDIS_URL': os.environ.get('REDIS_URL', ''),
}

That is the whole minimum, and both values may be empty at startup. The package needs them only when something actually reaches Telegram or Redis, so tests, migrations and a build all run without them.

A project with one bot writes nothing more. A second bot is a section under TELEGRAM_BOTS, which inherits everything above and overrides what it names:

TELEGRAM_BOTS = {
    'default': {'TOKEN': os.environ.get('TELEGRAM_BOT_TOKEN', '')},
    'support': {'TOKEN': os.environ.get('SUPPORT_BOT_TOKEN', '')},
}

See Multiple bots, and Dynamic bots where they arrive while the container runs.

The package ships tables of its own — the event log, the schedule an eta writes to, and the rows a project configures bots in — so run migrations after adding it. With EVENT_LOG_DATABASE naming a database of its own, migrate both: the feed lives there and everything else does not.

python manage.py migrate

The table is created whether or not you turn the event log on — EVENT_LOG is off by default, and nothing is written until you set it. See Event log.

Configure from the environment

Scalar settings can come from DJANGO_AIOGRAM_<NAME>:

# .env
DJANGO_AIOGRAM_TOKEN=123:abc
DJANGO_AIOGRAM_REDIS_URL=redis://redis:6379/0
DJANGO_AIOGRAM_ENABLED=0

One bot's own setting is DJANGO_AIOGRAM_<ALIAS>_<NAME>, so a bot's token can stay out of settings.py:

DJANGO_AIOGRAM_SUPPORT_TOKEN=456:def

The section still has to exist. TELEGRAM_BOTS is what declares which bots there are, and a variable is read only for an alias already declared there — so 'support': {} is the whole of what the settings need, and the environment fills it in.

Django settings win over the environment. Callables and mappings — DEFAULT_KWARGS, DEFAULT_BOT_PROPERTIES, RATE_LIMIT — have no sensible textual form and stay in settings.py.

Run the bot

# docker-compose.yml
services:
  telegram_bot:
    image: ${IMAGE}
    command: python manage.py start_tgbot
    restart: always
    env_file: .env
    depends_on: [redis]

  redis:
    image: redis:7-alpine
    restart: always

See Deployment for the whole file, and for turning the bot off in every other process.

Check the configuration

python manage.py check

Settings are validated: wrong types, unknown keys, misspelled bot properties and impossible rate limits all fail here rather than at the first message. Missing credentials are reported as warnings, not errors, so a build or a migration container is not blocked by them.

Run it somewhere the bot is enabled: a process with ENABLED off registers no checks, so it reports nothing either way — unless the event log is on there, which registers all of them on its own, bot settings included.

Next