Sign inSign up

drumsergio/telegram-archive

By drumsergio

β€’Updated about 23 hours ago

Image
1

10K+

drumsergio/telegram-archive repository overview

Telegram Archive β€” self-hosted Telegram backups

⁠Telegram Archive

Docker Pulls GitHub Stars License Release codecov

Automated Telegram backup with Docker. Performs incremental backups of messages and media on a configurable schedule.

This project is developed with AI assistance (Claude Code).

⁠Features

β πŸ“¦ Backup Engine
  • Incremental backups β€” Only downloads new messages since last backup
  • Scheduled execution β€” Configurable cron schedule (default: every 6 hours)
  • Real-time listener β€” Catch edits, deletions, and new messages instantly between backups
  • Album support β€” Groups photos/videos sent together as albums
  • Service messages β€” Tracks group photo changes, title changes, user joins/leaves
  • Forwarded message info β€” Shows original sender name for forwarded messages
  • Channel signatures β€” Displays post author when channels have signatures enabled
  • Media deduplication β€” Symlinks identical files to save disk space
  • Avatars always fresh β€” Profile photos updated on every backup run
⁠🎬 Media Support
  • Photos, videos, documents, stickers, GIFs
  • Voice messages and audio files with in-browser player
  • Polls with vote counts and results
  • Configurable size limits and selective download
⁠🌐 Web Viewer
  • Telegram-like dark UI β€” Feels like the real app
  • Mobile-friendly β€” Responsive design with iOS/Android optimizations
  • Integrated lightbox β€” View photos and videos without leaving the page
  • Keyboard navigation β€” Arrow keys to browse media, Esc to close
  • Real-time updates β€” WebSocket sync shows new messages instantly
  • Push notifications β€” Get notified even when browser is closed
  • Search β€” Find chats by name and messages by text, across the whole archive or inside one chat
  • JSON export β€” Download chat history with date range filters
β πŸ”’ Security & Privacy
  • Multi-user access control β€” Master account + DB-backed viewer accounts with per-user chat whitelists
  • Admin panel β€” Create, edit, delete viewer accounts with fine-grained chat permissions
  • Audit logging β€” Track all login attempts, admin actions, and API access
  • Authenticated media β€” Media files require login and respect per-user permissions
  • Mass deletion protection β€” Rate limiting prevents accidental data loss
  • Runs as non-root β€” Docker best practices
β πŸ—„οΈ Database
  • SQLite (default) β€” Zero config, single file
  • PostgreSQL β€” For larger deployments with real-time LISTEN/NOTIFY

β πŸ—ΊοΈ Roadmap

See docs/ROADMAP.md⁠ for what's planned, and docs/CHANGELOG.md⁠ for complete version history.

Have a feature request? Open an issue⁠!

β πŸ“Έ Screenshots

Click to view Desktop and Mobile screenshots
⁠Desktop

Desktop View

⁠Mobile
Mobile View

⁠Docker Images

Two separate Docker images are available (v4.0+):

ImagePurposeSize
drumsergio/telegram-archiveBackup scheduler (requires Telegram credentials)~300MB
drumsergio/telegram-archive-viewerWeb viewer only (no Telegram client)~150MB

πŸ“¦ Upgrading from v3.x? See Upgrading from v3.x to v4.0⁠ for migration instructions.

⁠Quick Start

⁠1. Get Telegram API Credentials
  1. Go to https://my.telegram.org/apps⁠
  2. Log in with your phone number
  3. Create a new application (any name/platform)
  4. Note your API ID (numbers) and API Hash (letters+numbers)
⁠2. Deploy with Docker
# Clone the repository
git clone https://github.com/GeiserX/Telegram-Archive
cd Telegram-Archive

# Create data directories
mkdir -p data/session data/backups
chmod -R 755 data/

# Configure environment
cp .env.example .env

Edit .env with your credentials:

TELEGRAM_API_ID=12345678          # Your API ID
TELEGRAM_API_HASH=abcdef123456    # Your API Hash  
TELEGRAM_PHONE=+1234567890        # Your phone (with country code)
VIEWER_USERNAME=admin             # Required for web access
VIEWER_PASSWORD=change-this       # Required for web access

Optional: enable a SOCKS5 proxy for all Telegram connections (useful in regions where Telegram is blocked or behind corporate firewalls)

TELEGRAM_PROXY_TYPE=socks5
TELEGRAM_PROXY_ADDR=127.0.0.1
TELEGRAM_PROXY_PORT=1080
TELEGRAM_PROXY_USERNAME=
TELEGRAM_PROXY_PASSWORD=
TELEGRAM_PROXY_RDNS=false
⁠3. Authenticate with Telegram

Option A: Using the provided scripts (recommended for fresh installs)

# Run authentication
./init_auth.sh    # Linux/Mac
# init_auth.bat   # Windows

Option B: Direct Docker command (for existing deployments or re-authentication)

If your session expires or you need to re-authenticate an existing container:

# Generic command - adjust volume paths and credentials
docker run -it --rm \
  -e TELEGRAM_API_ID=YOUR_API_ID \
  -e TELEGRAM_API_HASH=YOUR_API_HASH \
  -e TELEGRAM_PHONE=+YOUR_PHONE_NUMBER \
  -e SESSION_NAME=telegram_backup \
  -v /path/to/your/session:/data/session \
  drumsergio/telegram-archive:8.13.0 \
  python -m src auth

Example for docker compose deployment:

# If using docker compose with a session volume
docker run -it --rm \
  --env-file .env \
  -v ./data:/data \
  drumsergio/telegram-archive:8.13.0 \
  python -m src auth

# Then restart the backup container
docker compose restart telegram-backup

What happens during authentication:

  1. The script connects to Telegram's servers
  2. Telegram sends a verification code to your Telegram app (check "Telegram" chat)
  3. Enter the code when prompted
  4. If you have 2FA enabled, enter your password when prompted
  5. Session is saved to the mounted volume for future use
⁠4. Start Services
docker compose up -d

View your backup at http://localhost:8000⁠

The default compose binds the viewer to 127.0.0.1. Put it behind a reverse proxy only after setting VIEWER_USERNAME and VIEWER_PASSWORD. To deliberately run without auth for a local-only viewer, set ALLOW_ANONYMOUS_VIEWER=true β€” this grants read-only access only; writes still require the master account.

⁠Common Issues
ProblemSolution
Permission deniedRun chmod -R 755 data/
init_auth.sh: command not foundRun chmod +x init_auth.sh first
Viewer shows no dataBoth containers need same database path - see Database Configuration⁠
Failed to authorizeRe-run ./init_auth.sh

⁠Web Viewer

The standalone viewer image (drumsergio/telegram-archive-viewer) lets you browse backups without running the backup scheduler.

# Example: Viewer-only deployment
services:
  telegram-viewer:
    image: drumsergio/telegram-archive-viewer:8.13.0
    ports:
      - "127.0.0.1:8000:8000"
    environment:
      BACKUP_PATH: /data/backups
      DATABASE_DIR: /data/db
      VIEWER_USERNAME: admin
      VIEWER_PASSWORD: your-secure-password
      VIEWER_TIMEZONE: Europe/Madrid
    volumes:
      # SQLite needs write access for WAL files, sessions, audit logs, and thumbnails.
      # Use :ro only when the database is PostgreSQL and media is mounted separately.
      - /path/to/data:/data

Browse your backups at http://localhost:8000⁠

⁠Configuration

All settings are configured via environment variables. Set them in your .env file or as environment: entries in docker-compose.yml. See .env.example⁠ for a ready-to-use template.

ENABLE_LISTENER is a master switch. When set to false (the default), all LISTEN_* and MASS_OPERATION_* variables have no effect. You only need to configure those when you set ENABLE_LISTENER=true.

⁠Environment Variables

The Scope column shows whether each variable applies to the backup scheduler (B), the web viewer (V), or both (B/V).

VariableDefaultScopeDescription
Telegram Credentials
TELEGRAM_API_IDrequiredBAPI ID from my.telegram.org⁠
TELEGRAM_API_HASHrequiredBAPI Hash from my.telegram.org⁠
TELEGRAM_PHONErequiredBPhone number with country code (e.g., +1234567890)
TG_ACCOUNT_<N>_API_ID-BMultiple accounts⁠: API ID of account N. N starts at 1 and must be contiguous. Declaring any TG_ACCOUNT_* variable switches to indexed mode and the three legacy variables above are ignored
TG_ACCOUNT_<N>_API_HASH-BAPI Hash of account N
TG_ACCOUNT_<N>_PHONE_NUMBER-BPhone number of account N with country code. Must be distinct across accounts
TG_ACCOUNT_<N>_LABELdefault (N=1), account<N> (Nβ‰₯2)BOptional display label for account N
TG_ACCOUNT_<N>_SESSION_NAMEsee descriptionBOptional session file name for account N. Account 1 defaults to the legacy SESSION_NAME chain (so an upgraded deployment keeps its session file and never re-logins); accounts 2+ default to telegram_backup_account<N>
TELEGRAM_PROXY_TYPE-BOptional proxy type for all Telegram clients. Currently supports socks5
TELEGRAM_PROXY_ADDR-BSOCKS5 proxy host or IP address
TELEGRAM_PROXY_PORT-BSOCKS5 proxy port
TELEGRAM_PROXY_USERNAME-BOptional SOCKS5 username
TELEGRAM_PROXY_PASSWORD-BOptional SOCKS5 password
TELEGRAM_PROXY_RDNSfalseBUse remote DNS resolution through the SOCKS5 proxy
Backup Schedule & Storage
SCHEDULE0 */6 * * *BCron expression for backup frequency
BACKUP_PATH/data/backupsB/VBase path for backup data and media
DOWNLOAD_MEDIAtrueBDownload media files (photos, videos, documents)
DOWNLOAD_MEDIA_TYPES(empty)BComma-separated whitelist of media types worth downloading: photo, video, video_note, animation, voice, audio, sticker, document, webpage. Empty downloads every type. Filtered media is still recorded with its metadata (name, MIME, size), only the file stays on Telegram. An archive captured before 8.5.0 typed round videos as video, and the retry pass judges a stored row by its stored type, so run reclassify-round-videos once before relying on video_note here
DOWNLOAD_DOCUMENT_MIME_TYPES(empty)BNarrow the document type to specific MIME types, e.g. application/pdf. Exact match, or the filename extension derived from the configured MIMEs (catches files Telegram labels application/octet-stream but names report.pdf). Each value must be a full type/subtype: a wildcard or a bare extension is rejected at startup rather than matching nothing. The extension fallback comes from the system MIME database, so a type it does not know is matched by its declared MIME only, and says so at startup. Empty keeps every document
DOWNLOAD_CHAT_DESCRIPTIONfalseBFetch each chat's description on every run for the viewer's chat info panel: a group or channel's about text, a user's bio, plus the member count of channels and supergroups. One extra API request per chat per run
MAX_MEDIA_SIZE_MB100BSkip media files larger than this (MB)
MEDIA_MAX_FILENAME_BYTES143BUsable filename byte budget for downloaded media. Raise to 255 on plain ext4/xfs; keep 143 for Synology/eCryptfs encrypted shares
MEDIA_MAX_DOWNLOAD_ATTEMPTS5BStop retrying a file's download after this many failed attempts. Re-requesting the download resets the counter
MEDIA_FLOOD_SLEEP_THRESHOLD60BMid-download FloodWaits up to this many seconds are absorbed in place so the transfer resumes instead of restarting from byte 0 (issue #232). 0 restores the old raise-immediately behavior. Absorbed pauses count toward DOWNLOAD_TIMEOUT_SECONDS
DIALOG_FLOOD_SLEEP_THRESHOLD60BFloodWaits up to this many seconds during get_dialogs()'s internal pagination are absorbed in place so the listing resumes on the same page instead of the whole call restarting from page 1 (issue #295 β€” an account with enough dialogs to reliably trip a page's FloodWait could otherwise never complete an initial non-whitelist backup, no matter the retry count or schedule spacing). 0 restores the old raise-immediately behavior
DOWNLOAD_TIMEOUT_SECONDS3600BGive up on a single media download after this many seconds. 0 disables the timeout
MEDIA_REFRESH_MAX_ATTEMPTS3BHow many times a media item whose file reference expired is re-fetched and retried before it is left for the next scheduled run
MEDIA_REFRESH_TIMEOUT_SECONDS120BUpper bound on one message-refresh round trip, so a wedged connection cannot stall the run
PARALLEL_DOWNLOAD_ENABLEDfalseBFetch large files over several connections to lift the single-stream speed cap (see below)
PARALLEL_DOWNLOAD_MIN_SIZE_MB20BOnly files at least this large use the parallel path (min 1)
PARALLEL_DOWNLOAD_CONNECTIONS4BConcurrent connections per file (clamped 2–8)
PARALLEL_DOWNLOAD_PART_SIZE_KB512BChunk size per request; one of 4/8/16/32/64/128/256/512
BATCH_SIZE100BMessages processed per database batch
CHECKPOINT_INTERVAL1BSave backup progress every N batch inserts (lower = safer resume after crash)
DATABASE_TIMEOUT60.0B/VDatabase operation timeout in seconds
SESSION_NAMEtelegram_backupBTelethon session file name
SESSION_DIR/data/sessionBDirectory holding the session file. Defaults to a session/ directory alongside BACKUP_PATH
DEDUPLICATE_MEDIAtrueBSymlink identical media files across chats to save disk space
SYNC_DELETIONS_EDITSfalseBBatch-check ALL messages for edits/deletions each run (expensive!)
VERIFY_MEDIAfalseBRe-download missing or corrupted media files
FILL_GAPSfalseBAfter each scheduled backup, look for runs of missing message IDs and fetch them
GAP_THRESHOLD50BHow many consecutive missing message IDs count as a gap worth filling
STATS_CALCULATION_HOUR3BHour (0-23) to recalculate backup statistics daily
PRIORITY_CHAT_IDS-BComma-separated chat IDs to process first in all operations
SKIP_MEDIA_CHAT_IDS-BSkip media downloads for specific chats (messages still backed up with text)
SKIP_MEDIA_DELETE_EXISTINGtrueBDelete existing media files and DB records for chats in skip list to reclaim storage
DOWNLOAD_YOUTUBE_VIDEOSfalseBArchive the video file Telegram attaches to a YouTube link preview. Off by default; the message, link and thumbnail are archived either way
YOUTUBE_VIDEOS_DELETE_EXISTINGfalseBAlso delete YouTube link-preview videos already downloaded (needs DOWNLOAD_YOUTUBE_VIDEOS=false). Cannot be undone
SKIP_TOPIC_IDS-BSkip specific topics in forum supergroups (format: chat_id:topic_id,...)
LOG_LEVELINFOB/VLogging verbosity: DEBUG, INFO, WARNING/WARN, ERROR
LOG_CHAT_TITLESfalseBName the chat on the two per-chat progress lines: [27/27] Backing up: "My Group". Opt-in. Chat ids are never logged either way, a one-to-one chat is named by kind only (private chat) and never by the person, and titles are sanitised so a chosen title cannot forge a log line
Flood & Retry Tuning
MAX_FLOOD_RETRIES5BHow many times a Telegram call is retried after a FloodWait before it gives up
MAX_FLOOD_WAIT_SECONDS3600BA FloodWait longer than this is not waited out β€” the call fails instead
BACKOFF_MIN_SECONDS2.0BFirst delay of the exponential backoff used for transient connection errors
BACKOFF_MAX_SECONDS300.0BCeiling for that backoff delay
FLOOD_WAIT_LOG_THRESHOLD10BFloodWaits shorter than this are routine and logged at DEBUG instead of WARNING. 0 logs every one
Chat FilteringSee Chat Filtering⁠ below
CHAT_IDS-BWhitelist mode: backup ONLY these chats (ignores all other filters)
WHITELIST_RESOLVE_DIALOG_LIMIT1000BWhen a CHAT_IDS entry cannot be resolved (typically a DM on a fresh session), scan up to this many dialogs once to warm the entity cache β€” it then resolves permanently (issue #234). 0 disables
CHAT_TYPESprivate,groups,channelsBType-based mode: comma-separated chat types to backup
GLOBAL_EXCLUDE_CHAT_IDS-BExclude specific chats (any type)
GLOBAL_INCLUDE_CHAT_IDS-BForce-include specific chats (any type)
EXCLUDE_CHAT_IDS-BLegacy alias for GLOBAL_EXCLUDE_CHAT_IDS, read only when that variable is unset or empty
INCLUDE_CHAT_IDS-BLegacy alias for GLOBAL_INCLUDE_CHAT_IDS, read only when that variable is unset or empty
PRIVATE_EXCLUDE_CHAT_IDS-BExclude specific private chats
PRIVATE_INCLUDE_CHAT_IDS-BForce-include specific private chats
GROUPS_EXCLUDE_CHAT_IDS-BExclude specific groups
GROUPS_INCLUDE_CHAT_IDS-BForce-include specific groups
CHANNELS_EXCLUDE_CHAT_IDS-BExclude specific channels
CHANNELS_INCLUDE_CHAT_IDS-BForce-include specific channels
GLOBAL_INCLUDE_FOLDER_IDS-BForce-include every chat in this Telegram folder, across all types
PRIVATE_INCLUDE_FOLDER_IDS-BForce-include this folder's private chats
GROUPS_INCLUDE_FOLDER_IDS-BForce-include this folder's groups
CHANNELS_INCLUDE_FOLDER_IDS-BForce-include this folder's channels. See Folder-based include⁠
FOLLOW_CHAT_MIGRATIONSfalseBAutomatically adopt the new supergroup id when a tracked basic group is upgraded to a supergroup, so capture continues without editing include lists. When off, the sweep only warns. See Group β†’ supergroup migrations⁠
Real-time ListenerSee Real-time Listener⁠ below
ENABLE_LISTENERfalseBMaster switch β€” enables all LISTEN_* features below
LISTEN_EDITStrueBApply text edits in real-time
LISTEN_DELETIONSfalseBProcess deletion events from Telegram. Opt-in only
DELETION_MODEhardBWhen deletions are processed: hard removes archived messages (legacy), soft keeps messages and marks them deleted
LISTEN_NEW_MESSAGEStrueBSave new messages in real-time between scheduled backups
LISTEN_NEW_MESSAGES_MEDIAfalseBAlso download media immediately (vs. next scheduled backup)
LISTEN_CHAT_ACTIONStrueBTrack chat photo, title, and member changes
LISTEN_REACTIONSfalseBCapture reactions in real-time (opt-in). Best-effort and aggregate-only (per-emoji counts); the scheduled backup reconciles reactions regardless
REACTION_DEBOUNCE_SECONDS1.5BCoalesce a burst of reaction updates on the same message into one write
REACTION_RESWEEP_DAYS0BRe-check the last N days of messages per chat on every scheduled sweep to recover your own reactions (0 disables). See Reactions made by your own account⁠
REACTION_RESWEEP_MAX_PER_CHAT500BCap on messages re-checked per chat per sweep (β‰ˆ5 API calls/chat/sweep at the default)
REACTION_RESWEEP_BATCH_DELAY_SECONDS2BMinimum spacing between the re-sweep's API requests, across chats (0 disables). Smooths bursts; on a FloodWait the re-sweep pauses and resumes within the same run once the wait expires, deferring to the next sweep only if the wait outlives the run or floods repeat
MASS_OPERATION_THRESHOLD10BMax operations per chat before rate limiting triggers
MASS_OPERATION_WINDOW_SECONDS30BSliding window for counting operations (seconds)
MASS_OPERATION_BUFFER_DELAY2.0BDeprecated compatibility setting; operations are rate-limited, not buffered
Event Webhook
EVENT_WEBHOOK_ENABLEDfalseBMaster switch β€” fire an HTTP request when the listener applies an edit/deletion. Opt-in only. See Event Webhook⁠ below
EVENT_WEBHOOK_URLβ€”BTarget URL (http:// or https://). Required when enabled; treated as a secret and never logged
EVENT_WEBHOOK_METHODPOSTBPOST or PUT
EVENT_WEBHOOK_HEADERS{}BJSON object of extra headers (auth tokens etc.). Its Content-Type drives auto-escaping; defaults to application/json; charset=utf-8
EVENT_WEBHOOK_EVENTSbothBComma list: message_edited, message_deleted
EVENT_WEBHOOK_CHAT_IDSβ€”BComma-separated marked chat ids to fire for; empty = all chats the listener processes
EVENT_WEBHOOK_BODY_TEMPLATEJSON bodyBCustom body with {placeholder} / {placeholder|filter} substitution; empty = default JSON body
DatabaseSee Database Configuration⁠ below
DATABASE_URL-B/VFull database URL (highest priority, overrides all below)
DB_TYPEsqliteB/VDatabase engine: sqlite or postgresql
DB_PATH$BACKUP_PATH/telegram_backup.dbB/VPath to SQLite database file
DATABASE_PATH-B/VFull path to SQLite file (v2 compatible alias for DB_PATH)
DATABASE_DIR-B/VDirectory containing telegram_backup.db (v2 compatible)
POSTGRES_HOSTlocalhostB/VPostgreSQL host
POSTGRES_PORT5432B/VPostgreSQL port
POSTGRES_USERtelegramB/VPostgreSQL username
POSTGRES_PASSWORD-B/VPostgreSQL password (required when using PostgreSQL)
POSTGRES_DBtelegram_backupB/VPostgreSQL database name
DB_ECHOfalseB/VLog every SQL statement. Debugging only β€” extremely verbose
Viewer & Authentication
VIEWER_USERNAME-VMaster web viewer username
VIEWER_PASSWORD-VMaster web viewer password
ALLOW_ANONYMOUS_VIEWERfalseVExplicitly allow unauthenticated local viewer mode. Grants read-only access β€” browsing/search work, but settings, viewer/token management, and deletions still require the master account
AUTH_SESSION_DAYS30VDays before re-authentication is required
AUTH_PROXY_HEADER-VHeader carrying the authenticated username from a trusted reverse proxy (Authelia, Authentik, Keycloak), e.g. Remote-User. See warning below
AUTH_PROXY_ADMIN_USERS-VComma-separated usernames from AUTH_PROXY_HEADER that get the admin (master) role
AUTH_PROXY_DEFAULT_ACCESSnoneVDefault chat access for auto-created proxy users: none or all
DISPLAY_CHAT_IDS-VRestrict viewer to specific chats (comma-separated IDs)
TRUST_PROXY_HEADERSfalseVTrust X-Forwarded-For / X-Real-IP only when your reverse proxy overwrites them
INTERNAL_PUSH_SECRET-B/VShared secret for SQLite backup-to-viewer realtime push over Docker/private networks
VIEWER_HOSTlocalhostBViewer host for SQLite realtime push from backup/listener. The localhost default only fits a same-host (bare-metal) setup; the shipped compose overrides it to the telegram-viewer service
`VIEWER

Tag summary

Content type

Image

Digest

sha256:80b619f3f…

Size

236.2 MB

Last updated

about 23 hours ago

docker pull drumsergio/telegram-archive