Telegram Files: a self-hosted downloader built on TDLib and Vert.x
A self-hosted Telegram file downloader for continuous, stable, and unattended downloads.
At a glance
- What is it?
- Telegram Files is a Java and Next.js application that logs into Telegram user accounts, watches channels and groups, and writes files to local storage you control. It is aimed at unattended, always-on downloading rather than one-off grabs, and its main friction is that first-administrator bootstrap.
- Who is it for?
- Adopt Telegram Files if you need an unattended service that pulls files out of Telegram channels and groups onto storage you own, and you are willing to run a reverse proxy that preserves X-Real-IP, X-Forwarded-Host and X-Forwarded-Proto. Do not adopt it if your need is a one-off download or a purely cloud-side archive, since operating a TDLib-backed service, a database and a web front end is more moving parts than a desktop client.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 20 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Telegram Files addresses: unattended downloads from channels and groups
Telegram is a distribution channel for files as much as it is a chat product, and the desktop and mobile clients are built around reading, not around archiving. Downloading a large video or a long series of attachments from a channel means keeping a client open, watching for new posts, and moving files into a folder structure by hand. Telegram Files exists to take that loop out of the client and put it on a server. The README describes it as "a self-hosted Telegram file downloader for continuous, stable, and unattended downloads", and the feature list backs that framing: downloads from channels and groups, multiple Telegram accounts managed and downloading simultaneously, pause and resume, automatic transfer to designated destinations, and fetching files from Telegram shared links. The audience is the self-hosting crowd specifically. The repository carries topics for docker, unraid and self-hosted, and there is an install path through unRaid Community Repositories, which tells you the expected operator already runs a home server or a small VPS and is comfortable with containers. It is not a tool for someone who wants a file from one chat once; a desktop client does that better and with less setup.
How the pieces fit: TDLib for Telegram, Vert.x for the API, Next.js for the UI
The stack is split into two halves in the repository layout. The api directory is a Java service built with Gradle and targeting JDK 23; the web directory is a TypeScript front end built with npm, and the topics list names Next.js and shadcn-ui, so the UI is a React application rather than server-rendered templates. Telegram connectivity comes through TDLib, listed as a topic, which is the library Telegram publishes for clients that need to act as a real user account rather than a bot. That choice is why the project can read channels and groups that a bot cannot join, and it is also why setup requires an api id and hash: the README says plainly that before getting started you should apply for credentials on the Telegram API page at my.telegram.org/apps. Those credentials identify the application, and each Telegram account you add then authenticates through the UI. The server side runs on Vert.x, which handles the HTTP API, the file preview endpoints and the WebSocket connections the front end uses for live state. Storage is local: the Docker example mounts ./data into /app/data and sets APP_ROOT to /app/data, so downloaded files, session state and the database live under one directory you can back up or move. The README also notes that the file table uses virtual lists and that file information is preloaded to support searches, which are front-end concerns that matter once a channel has tens of thousands of messages. The roadmap items for automatic rule-based downloading, download statistics and reports, and auto transfer to other destinations are all marked complete in the README, so those are shipped rather than planned.
Running it: the deploy script, the Docker command and the environment keys
The recommended path is an interactive shell script, which the README marks as recommended and limits to Linux on amd64 and arm64. You run it with curl piped to bash from the raw GitHub URL, and it supports subcommands directly: start installs and starts the service, stop, update, restart, status, logs and config do what their names suggest. If you prefer to see the container definition, the README gives a docker run example that publishes port 6543 on the host to port 80 in the container, sets --restart always, and passes five environment variables: APP_ENV defaulting to prod, APP_ROOT defaulting to /app/data, HTTP_SECURE_COOKIES defaulting to false, and TELEGRAM_API_ID and TELEGRAM_API_HASH, which you must supply. The image is ghcr.io/jarvis2f/telegram-files:latest. A docker-compose path also exists: copy docker-compose.yaml and .env.example into your project directory and run docker-compose up -d. On unRaid, the README says to search for telegram-files in Community Repositories. The security note in the README is the part worth reading twice. Management APIs, file previews and WebSockets all require an administrator session, and the README asks you to use HTTPS, configure HTTP_ALLOWED_ORIGINS and complete the first-administrator bootstrap before exposing the service. That bootstrap is a 15-minute one-time code printed by the API on first start. You open the UI from loopback or the same private LAN and create the first administrator there. A reverse proxy in front of the service must preserve X-Real-IP, X-Forwarded-Host and X-Forwarded-Proto; the bundled Nginx configuration already does this, which is a hint that the bundled config is the intended deployment shape rather than an arbitrary proxy.
Password recovery is local-only, and that shapes how you operate it
The README documents password recovery as a local operation that revokes every active session, run against the built jar rather than through the web UI. The two commands are java -cp api/build/libs/telegram-files.jar telegram.files.Maintain admin reset-password owner followed by java -cp api/build/libs/telegram-files.jar telegram.files.Maintain admin apply-reset owner. Two properties follow from that. First, recovery requires shell access to the host, so a lost password is not a support ticket; it is an SSH session. Second, because the reset revokes all sessions, anyone currently logged into the dashboard is logged out when you run it. That is a sensible default for a service that holds Telegram session material and can read your channels, but it means you should not treat the reset command as a casual fix for a forgotten password while other people are using the instance. The command also tells you something about the deployment: it references a jar path under api/build/libs, which is the build output, so this recovery route assumes either a source build or a container you can exec into with the jar present. The README does not spell out the exact invocation inside the published image, so verify that before you need it rather than after.
Where it is the wrong tool, and what the documentation does not settle
The clearest limitation is operational weight. Telegram Files is a Java service, a TDLib dependency, a web front end and a data directory that holds both downloaded files and session state. If you want to grab a single file from a channel you already belong to, a Telegram desktop client downloads it in two clicks and stores nothing extra. The project only earns its keep when the pattern is recurring: new posts arriving in channels you want mirrored, multiple accounts, transfers to a destination. A second boundary is account risk. Because it authenticates as a user account through TDLib rather than as a bot, the accounts you add are the ones doing the downloading, and the README does not describe rate limiting, per-account throttling or any guidance on how aggressive concurrent downloads can be. The feature list says multiple accounts can download simultaneously, which is a capability statement, not a safety statement. Treat the number of accounts and the concurrency level as something you tune carefully rather than something the project has documented a safe ceiling for. Third, the README is thin on storage. It shows a single volume mount and APP_ROOT, but there is no discussion of what happens when the disk fills, whether partial downloads are cleaned up, or how the automatic transfer to other destinations is configured. Those are exactly the questions a continuous downloader raises, and the supplied material does not answer them. Finally, the README does not state supported Telegram content types beyond videos and images in the preview feature, so do not assume every attachment type is handled equally well.
The alternative: running a Telegram client or a bot instead
The obvious alternative is a Telegram desktop client with its download folder pointed at a synced directory. The difference in approach is where the work happens. A desktop client is an interactive application: you are present, you click, the file lands on disk, and the client stops mattering. Telegram Files inverts that. It runs as a service with its own API, its own administrator account, its own session handling and its own storage root, and it keeps running whether or not anyone is looking at it. That inversion is the whole value proposition, and it is also the whole cost. A second alternative is a bot built on the Telegram Bot API. A bot is far easier to host and needs no api id and hash from my.telegram.org, but a bot can only see chats it has been added to and cannot browse arbitrary channels the way a user account can. Since the reason to run Telegram Files is usually a channel you do not administer, the bot route often does not reach the content at all. The choice is therefore not about which is better engineered; it is about whether the content you want is reachable by a bot, and whether you want a persistent service or a manual step.
Maintenance, versioning and the MIT licence
The release cadence visible in the supplied material is uneven rather than rapid: 0.3.0 in December 2025, 0.3.1 in June 2026, and 0.4.0 in August 2026, with the last push to the repository on the same day as the 0.4.0 release. That is a project that ships when there is something to ship, not on a schedule, and the version numbers are still in the 0.x range, which conventionally signals that the maintainers reserve the right to change things. The deploy script includes an update subcommand that pulls the latest image and restarts, so upgrades are a single operation, but the README does not describe a migration procedure for the data directory or the database between versions. Back up ./data before running update, and read the release notes for 0.4.0 before you do. On licensing: the repository is MIT, which is permissive and places few obligations on how you run or redistribute the software. That is the extent of what can be said here. MIT does not settle your relationship with Telegram, and nothing in the supplied material describes the terms under which Telegram permits automated downloading through a user account. That question is yours to resolve, and it sits outside the software licence entirely.
Editorial conclusion
Adopt Telegram Files if you need an unattended service that pulls files out of Telegram channels and groups onto storage you own, and you are willing to run a reverse proxy that preserves X-Real-IP, X-Forwarded-Host and X-Forwarded-Proto. Do not adopt it if your need is a one-off download or a purely cloud-side archive, since operating a TDLib-backed service, a database and a web front end is more moving parts than a desktop client. Before exposing anything, verify the bootstrap step: on first start the API prints a 15-minute one-time code, and the first administrator must be created from loopback or the same private LAN before the service is reachable from outside.
Community notes