A self-hosted Telegram bot that generates secure, rate-limited download links for uploaded files, with time-based expiration (TTL), an optional admin dashboard, and automatic cleanup.
The project can run locally, on a VPS, or on any cloud platform.
For the easiest setup, Railway is recommended.
This repository is designed to be deployed directly as a Railway template.
- Free public domain (
*.railway.app) included - Automatic HTTPS (no SSL setup needed)
- One-click PostgreSQL & Redis
- Easy environment variable management
- No server maintenance or manual provisioning
- Click Deploy on Railway
- Railway will create a new project from this template
- Add PostgreSQL and Redis plugins
- Set the required environment variables
- Optional: Enable Persistent Storage (Railway Buckets)
- Deploy
Your bot and download API will be live within minutes.
You can still deploy this project locally or on any VPS.
- Upload files via Telegram and receive a public download link
- Supports documents, videos, audio, photos, animations, voice, and video notes
- Preserves original file quality
- Optional private bot mode (allowed user IDs)
- Built with Pyrogram / Pyrofork
- Unique file IDs
- Direct downloads via FastAPI
- Correct filenames and headers
- Supports HTTP range requests (206 Partial Content)
- Optional expiration per file
- Time-based expiration only (no download limits)
- Unlimited downloads are always allowed
- Files expire automatically when TTL is reached
- Limits how many file uploads are processed at the same time
- Prevents server overload and Telegram flood limits
- Extra uploads are automatically queued
- Fully configurable via environment variables
TTL is controlled per user via Telegram commands.
Examples:
/mode ttl 30β 30 minutes/mode ttl 2hβ 2 hours/mode ttl 1dβ 1 day/mode ttl 0or/mode resetβ Never expire
TTL is stored internally in seconds.
Enabled only if ADMIN_ENABLED=true.
Features:
- Secure session-based login
- View total files, downloads, and active files
- Search files by name
- Disable files (expire immediately)
- Delete files
- View top downloads, recent uploads, and expiring files
- Light / Dark mode toggle
When enabled, the admin dashboard is available at:
https://your-domain.com/admin
If
ADMIN_ENABLED=false, the admin dashboard routes are not registered and the bot still works normally.
- Background task removes expired files
- Cleans database records and Redis cache
- Deletes expired objects from S3-compatible storage automatically
- Safe against partial failures
- Global per-IP rate limiting
- Redis-backed
- Proper
Retry-Afterheaders
This project supports two storage backends, selectable via environment variables.
- Files are stored in the
uploads/directory - Suitable for local development and small deployments
- Files are removed automatically when TTL expires
- Files are stored in an S3-compatible bucket
- Supports Railway Storage Buckets, AWS S3, Cloudflare R2, and similar services
- Files are served via presigned URLs
- No service egress for downloads
- Files persist across deployments
- Automatic cleanup when TTL expires
On Railway, S3 credentials are injected automatically when you connect a Storage Bucket.
Enable S3 storage by setting:
STORAGE_BACKEND=s3
If you are using S3-compatible storage outside of Railway (e.g. AWS S3, Cloudflare R2, MinIO, Backblaze B2), you must provide the following environment variables:
AWS_ENDPOINT_URL=https://storage.example.com
AWS_S3_BUCKET_NAME=your-bucket-name
AWS_DEFAULT_REGION=auto
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key- Python 3.11+ (tested on 3.13)
- FastAPI
- Pyrogram / Pyrofork
- PostgreSQL (asyncpg)
- Redis
- Jinja2
- Vanilla HTML / CSS / JS
- S3-compatible object storage
This project automatically creates and maintains its database schema at startup.
Migrations are intentionally omitted to keep the template simple and easy to deploy.
For larger or multi-tenant deployments, adding a migration system is recommended.
Create a .env file in the project root.
Tip: Rename
.env.exampleto.envand fill in your values.
API_ID=your_api_id
API_HASH=your_api_hash
BOT_TOKEN=your_bot_token
MAX_CONCURRENT_TRANSFERS=3
DATABASE_URL=postgresql://user:password@localhost:5432/filelink
REDIS_URL=redis://localhost:6379
Choose where uploaded files are stored.
Local filesystem (default):
STORAGE_BACKEND=local
S3-compatible storage:
STORAGE_BACKEND=s3
If not set, the bot defaults to local filesystem storage.
Local development:
BASE_URL=http://localhost:8000
Railway:
BASE_URL=${RAILWAY_PUBLIC_DOMAIN}
Custom domain:
BASE_URL=https://files.example.com
GLOBAL_RATE_LIMIT_REQUESTS=60
GLOBAL_RATE_LIMIT_WINDOW=10
ALLOWED_USER_IDS=123456789
ADMIN_ENABLED=true
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=change-me-now
SESSION_SECRET=change-me
pip install -r requirements.txt
uvicorn app.main:app --reload --log-level warning
docker build -t telegram-file-link-bot .
docker run -d --env-file .env -p 8000:8000 telegram-file-link-bot
Apache License 2.0

