---
title: "Deploy Teleproxy [Updated Sep '26]"
description: "Teleproxy — Self-Hosted MTProto Proxy for Telegram, Fake-TLS DPI Resistant"
category: "Other"
url: https://railway.com/deploy/teleproxy-mtproxy
---

# Deploy Teleproxy [Updated Sep '26]

Teleproxy — Self-Hosted MTProto Proxy for Telegram, Fake-TLS DPI Resistant

**[Deploy Teleproxy [Updated Sep '26] on Railway](https://railway.com/template/teleproxy-mtproxy)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/teleproxy-mtproxy/manifest.json

- **Creator:** shruistic
- **Category:** Other

## Template content

### teleproxy https://teleproxy.github.io/assets/logo.svg

- **Image:** ghcr.io/teleproxy/teleproxy:latest

## Documentation

# Deploy and Host Teleproxy (MTProto Proxy) on Railway

Teleproxy is an open-source MTProto proxy for Telegram — a small, high-performance relay that disguises your proxy traffic as ordinary HTTPS to a domain of your choosing, so network-level censorship that blocks Telegram directly has a much harder time blocking this. This template deploys it as a single container, verified live against the app's own startup script rather than guessed from documentation.

## About Hosting Teleproxy

Teleproxy's fake-TLS camouflage means connections to your proxy look, at a glance, like a normal HTTPS handshake to whatever domain you set as `EE_DOMAIN`. Combined with DPI resistance built into the proxy engine itself, this makes Teleproxy a meaningfully harder target to block than a plain, unobfuscated proxy. Self-hosting it on Railway means you control the secret, the camouflage domain, and the underlying server — nothing routes through a third party.

## How MTProto Fake-TLS Actually Works

MTProto is Telegram's own transport protocol — the format its official clients use to talk to Telegram's servers. An MTProto proxy is a relay that forwards this specific traffic on your behalf, which is a narrower job than a general-purpose SOCKS or HTTP proxy, but that narrowness is exactly what makes the obfuscation effective: since the proxy only ever needs to speak one protocol, it can specialize in disguising that one protocol convincingly.

There are effectively three generations of MTProto obfuscation, distinguished by the format of the connection secret:

- **Plain MTProto** (no obfuscation): traffic is forwarded as-is, and any ISP running deep packet inspection can fingerprint the packet structure almost immediately. In networks that actively filter Telegram, plain MTProto gets blocked within seconds.
- **Obfuscated MTProto** (`dd`-prefixed secrets): randomizes enough of the traffic to defeat casual inspection. This was generally sufficient protection through the early 2020s.
- **Fake-TLS** (`ee`-prefixed secrets): the connection imitates a genuine TLS handshake to a real domain — the one you set as `EE_DOMAIN`. To a firewall doing DPI, the traffic reads as someone visiting an ordinary HTTPS site, not as Telegram traffic at all.

This template uses fake-TLS by default — the `ee` prefix and the domain-hex encoding get constructed automatically in the connection link Teleproxy generates, built directly from your `EE_DOMAIN` and `SECRET` values. You don't need to hand-construct anything.

Worth being precise about what this hides: the proxy operator never sees message content — Telegram's own end-to-end and client-server encryption stays intact regardless of proxy. Fake-TLS hides the *fact that you're using Telegram*, from network observers between you and the proxy. It doesn't hide your connection from the proxy operator itself, which is exactly why self-hosting matters: no third party sees your connection metadata.

## Why Deploy Teleproxy on Railway

Running an MTProto proxy normally means renting a small VPS specifically for this one purpose, SSHing in, and manually running a Docker command. Railway collapses that into a one-click deploy with persistent storage for the proxy's config cache and a real public TCP endpoint, generated automatically.

The reference version of this template on Railway's marketplace currently sits at 68% health, and it's worth understanding why before deploying it as-is: its configuration ships with **no networking set up at all**. It deploys successfully every time — the container boots clean — but nothing is ever exposed publicly, so no Telegram client can actually reach it. It's not broken so much as inert. This template fixes that directly.

## This Template's One Real Finding

Getting to a working fix took more than "just add a TCP proxy," since the wrong port was surprisingly easy to assume. The app's own logs print `Started as [IP:8888:worker_id:timestamp]` for every worker — including the main one — which looks like confirmation that 8888 is the real listener. It isn't; that's a worker identifier, not a bind address. The real answer came from reading the container's own `start.sh` directly: `PORT=${PORT:-443}` is the actual MTProto port; `STATS_PORT=${STATS_PORT:-8888}` is a separate stats/QR server, bound to `127.0.0.1` only, and never externally reachable.

Worth naming a real dead end hit along the way: an earlier attempt targeted port 8888 with a TCP proxy staged before first boot, and that reliably crashed with `bind(): Address in use` — reproduced identically on a brand-new project, ruling out leftover state. That looked like a platform-level bug. It wasn't. Repeating the same test on the *correct* port, 443, deployed clean with zero crashes, verified twice. The crash was entirely due to the wrong port, not a Railway issue — meaning a TCP proxy on 443 is safe to pre-configure directly in this template.

## Understanding the Configuration Variables

`EE_DOMAIN` is the most important variable for the camouflage. It should be a real, reachable HTTPS domain not served through a CDN like Cloudflare (fake-TLS is incompatible with shared-IP CDN proxying) — the default, `www.microsoft.com`, works well. At startup Teleproxy fetches that domain's real TLS response to shape its fake handshake; if the fetch fails (seen during this template's own testing, logged as `Failed to parse upstream TLS response`), it falls back to default response settings and keeps running — non-fatal.

`SECRET` is your connection credential, auto-generated as a 32-character hex string — treat it like any shared credential. Teleproxy also supports numbered `SECRET_1` through `SECRET_16` variables for issuing distinct, independently revocable credentials per person or device.

`WORKERS` controls how many worker processes handle connections concurrently — 4 is a reasonable default, raise it for higher volume.

`DIRECT_MODE` (default `false`) routes through Telegram's official "ME" relay; `true` connects straight to Telegram's data centers instead, but is incompatible with `PROXY_TAG`.

`PROXY_TAG` is optional and ties into Telegram's proxy-sponsorship mechanism for promoting a channel. Leave it blank unless you want that.

## Common Use Cases

- **Bypassing network-level Telegram blocks**: fake-TLS camouflage makes proxy traffic harder to distinguish from normal HTTPS
- **Running a private proxy for personal or family use**: your own secret, your own server, no third-party proxy operator
- **Issuing separate, revocable credentials**: numbered `SECRET_N` variables let you hand out distinct connection links per person or device
- **Sharing proxy access via `PROXY_TAG`**: optionally promote a channel through your proxy's connection link

## Dependencies for Teleproxy Hosting

Just one service — no database, no cache. The volume at `/opt/teleproxy/data` caches Telegram's proxy config so it isn't re-fetched from `core.telegram.org` on every restart.

### Implementation Details

The template runs the official `ghcr.io/teleproxy/teleproxy:latest` image, verified against its actual entrypoint (`/opt/teleproxy/start.sh`) rather than assumed from docs. A TCP proxy on port 443 is included in the networking config — confirmed safe to pre-stage before first boot. Deploy logs print the ready-to-share `tg://proxy?...` link automatically.

## How Teleproxy Compares to General-Purpose Circumvention Tools

Against Shadowsocks, a general-purpose SOCKS5 proxy: Shadowsocks carries any traffic, not just Telegram, but its distinct pattern has grown more fingerprintable to advanced DPI over time. Teleproxy is narrower by design — Telegram only — and its fake-TLS mode directly targets that weakness.

Against V2Ray/Xray, a larger, more flexible framework: its configuration surface is substantially bigger, which pays off for general internet circumvention. For reliable Telegram access specifically, Teleproxy's handful of variables gets there with far less setup.

## Getting Started

Deploy the template. Railway assigns your proxy a public `domain:port` pair via the TCP proxy on port 443. Check the deploy logs for the printed `tg://proxy?...` connection link, or build one from your assigned endpoint and `SECRET`. Share that link (or the equivalent QR code) with whoever needs proxy access.

## Frequently Asked Questions

### Why does this template need a TCP proxy instead of a normal HTTP domain?
MTProto isn't HTTP — Telegram clients connect over raw TCP, so this needs a TCP proxy (a public `domain:port` pair) rather than an HTTP-routed domain.

### Is port 8888 usable for anything external?
No. It's the stats/QR-code server, and it's bound to `127.0.0.1` inside the container by design — confirmed directly from the app's own startup script. No TCP proxy configuration can make it externally reachable.

### Can the proxy operator read my Telegram messages?
No. Telegram's own encryption is unaffected by which proxy you use — the proxy only sees encrypted data. It can see metadata: your IP and connection timing, which is why self-hosting matters if that metadata concerns you.

### Why did the reference template end up with poor health?
Its configuration has no networking set up at all — it deploys successfully but is completely unreachable by any Telegram client. This template's own composer config includes the correct TCP proxy from the start.

### What happens if EE_DOMAIN can't be reached during startup?
Teleproxy tries to fetch that domain's real TLS response to make the fake handshake more convincing. If the fetch fails, it logs a warning and falls back to default response settings — the proxy still starts and works normally.

### Can I issue different connection credentials to different people?
Yes — use the numbered `SECRET_1` through `SECRET_16` variables instead of (or alongside) `SECRET`. Each gets its own connection link, and each can be revoked independently by clearing that one variable, without affecting anyone else's access.

## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/teleproxy-mtproxy
