TMD refers to telegrammediadownloader. It can download media from Telegram chats according to your configuration, and it also offers a web interface and optional bot control.
I ran into trouble when I applied for my Telegram app credentials. I tried different networks, and later got a friend to help. I'm keeping this as a personal record, but you shouldn't take it as proof that any failed application is due to a "dirty IP," and I wouldn't recommend borrowing someone else's api_id and api_hash.
First, tell the three credentials apart
api_id is the numeric ID of a Telegram app, and api_hash is the hash for that same app. You get both from my.telegram.org/apps. bot_token comes from BotFather after you create a bot, and it's only used for the optional bot features.
Don't mix up api_id, api_hash, and the bot_token from BotFather. Never send your own app credentials, login codes, or session files to anyone else, and never commit them to a public repository.

Illustration: Telegram downloads — keep credentials, sessions, and files separate
Set up the directories and config
Start by copying the full config from the official example, then edit the fields you need. What follows is just an excerpt of the relevant fields — don't use it as a complete file to overwrite yours with:
api_id: 123456
api_hash: YOUR_OWN_API_HASH
save_path: /app/downloads
web_host: 0.0.0.0
web_port: 5000
web_login_secret: REPLACE_WITH_A_LONG_RANDOM_SECRET
allowed_user_ids:
- me
The api_id number here is just an example. If you want bot control, configure your own bot_token and the accounts allowed to use it. Set the download targets, save paths, and media types according to the full template, and only download content you have the right to access and save.
Using Docker Compose
If you're using the project image directly, you don't need to include build: . as well. That option requires a local build context, which usually isn't there when you've just copied a compose file from a tutorial.
services:
telegram_media_downloader:
image: tangyoha/telegram_media_downloader:latest
restart: unless-stopped
ports:
- "127.0.0.1:8066:5000"
volumes:
- ./config.yaml:/app/config.yaml
- ./data.yaml:/app/data.yaml
- ./downloads:/app/downloads
- ./sessions:/app/sessions
- ./temp:/app/temp
- ./log:/app/log
Before running, prepare real config.yaml and data.yaml files following the project example — don't let Docker create directories with those names automatically. The download, session, and log directories also need the correct read/write permissions for the container user.
Here the host entry point is restricted to 127.0.0.1, not opened to the whole internet. To use it from another computer, you can forward it over SSH, or deploy it on a trusted internal network behind an authenticated reverse proxy.
First login and running in the background
The first time, you need to enter your account and verification code interactively. Run:
docker compose run --rm telegram_media_downloader
Follow the project's prompts in the terminal to log in, confirm the session is written to the persistent directory, then stop this foreground run and start the background service:
docker compose up -d
docker compose logs --tail=100 telegram_media_downloader
Don't lose the session file from the first run, or you may still need to log in again after recreating the container. Don't mount a session generated just to "get it running" in a directory that gets wiped.
If you're on a remote server, run the following forward on your own computer, then open http://127.0.0.1:8066. Replace your-server with the real server address:
ssh -L 8066:127.0.0.1:8066 your-user@your-server
Which layer to check when something goes wrong
If login fails, first check your own app credentials, account status, and verification code flow. If the web page won't open, first check whether the container is listening on 5000, the port mapping, and the SSH forward. If nothing downloads, then check the session config, file types, target permissions, and logs. These are not the same kind of problem.
Rclone is an optional tool for transferring files after the download finishes. Get local downloads and persistence working first, then configure the remote and mount path separately. Don't confuse $HOME on the server with paths inside the container.
References: project README, Compose example. This article covers checking the configuration; it did not log into your Telegram account or download media on a real machine.