
Deploy Polymarket Bot [Updated Sep '26]
Polymarket Copy-Trading Bot — Self-Hosted | Buy & Sell with Auto Cashout
polymarket-bot
Just deployed
/data
Deploy and Host Polymarket Bot V2 on Railway
Polymarket Bot V2 is a TypeScript copy-trading bot for Polymarket — it watches selected trader wallets and mirrors their trades under your own spend limits, with optional auto-redeem once a market resolves. This template deploys a patched fork, verified live against both the actual crash the upstream reference has and the fix for it.
About Hosting Polymarket Bot V2
The bot runs as a single always-on Node.js process: it polls Polymarket's activity feed for trades from wallets you specify, applies your copy strategy (percentage of size, fixed USD, or fixed shares) within hard daily-volume and per-position caps, and optionally redeems resolved positions automatically. Nothing about your wallet or credentials passes through a third-party operator — you provide your own Polymarket-linked private key, and the bot runs entirely under your control.
Why Deploy Polymarket Bot V2 on Railway
The reference version of this template on Railway's marketplace currently sits at 33% health, noticeably worse than a typical config-only issue. Digging into why led to an actual bug in the bot's own startup code, not a Railway networking problem.
This Template's Root Cause and Fix
In the upstream source (src/index.ts), main() wraps only the call to loadConfig() in a try/catch, and only handles its own ConfigError type gracefully — everything else re-throws and hits a bottom-level main().catch() that calls process.exit(1). The problem: new Wallet(PRIVATE_KEY) gets called twice outside any graceful handling — once inside loadConfig() itself (to derive a fallback profile address), and again inside ClobService.init(), which runs entirely outside the try/catch block. Either call throws a plain ethers error, not a ConfigError, the moment PRIVATE_KEY isn't a valid 32-byte hex string.
That's a very reachable failure mode here specifically, because PRIVATE_KEY is a long hex secret most users copy-paste from Polymarket's Magic key-reveal page (reveal.magic.link/polymarket) — a stray leading space, a missing 0x, or one dropped character is enough. Combine that with roughly twenty required fields total (private key, profile address, trader list, and — if auto-redeem is on — an RPC URL plus three separate Builder API credentials), and the odds that at least one field gets mistyped on a first deploy climb fast. With restartPolicyMaxRetries: 10 and no graceful path for this specific error, a mistyped key means ten identical crashes and a deployment that shows FAILED.
The fix applied in this fork is a small, surgical change to the bottom-level catch handler: instead of exiting on anything that isn't a ConfigError, it now treats every startup failure the same way — log it clearly, then idle:
main().catch((err) => {
const label = err instanceof ConfigError ? "config" : "startup";
const message = err instanceof Error ? err.message : String(err);
console.error(`[${label}] ${message}`);
void idleLoop();
});
Verified Live, Before and After
Both scenarios were deployed with the exact same deliberately malformed PRIVATE_KEY (0x1234, correct prefix, wrong length) and otherwise-valid surrounding values, so the comparison isolates the one variable:
- Unpatched upstream code: deployment status
CRASHED. Logs show the stack trace originating atnew Walletinside bothloadConfigand theClobServicepath, ending inprocess.exit(1), repeated across restart attempts. - This fork's patched code: deployment status
SUCCESS. Logs show[startup] invalid private key (argument="privateKey", ..., code=INVALID_ARGUMENT, ...)followed byFix configuration and restart the bot...— the process stays up, waiting for a corrected redeploy, instead of crash-looping.
Common Use Cases
- Copy-trading a single high-signal trader: mirror one wallet's positions with hard spend limits
- Diversifying across multiple traders: mirror several wallets at a fixed percentage each
- Running a self-custodied bot: your own private key, your own server, no third-party operator holding your credentials or your funds
- Auto-redeeming resolved positions: optional, requires a Polygon RPC endpoint and Polymarket Builder credentials
Dependencies for Polymarket Bot V2 Hosting
Just one service — no database, no cache. A persistent volume at /data stores state.json, tracking which trades and redemptions have already been processed so a restart doesn't re-execute them.
Implementation Details
This template builds from source (Node 22 via Railpack) rather than a prebuilt image, since the bot has no published Docker image. RAILPACK_NODE_VERSION=22 is set explicitly — a couple of dependencies warn or fail to install below Node 20.10, and the reference build runs Node 18 by default without it.
Why Self-Hosting Matters Here Specifically
Custodial copy-trading services — where you hand a third party your credentials or funds and let them execute trades on your behalf — carry a real, documented risk beyond the usual "trust a company with your data" concern: a widely reported supply-chain attack against one large custodial copy-trading operator resulted in a roughly $230,000 loss in a single incident. Self-hosting removes that specific failure mode entirely, since your private key never leaves infrastructure you control, and no third party ever holds custody of your funds.
How Polymarket Bot V2 Compares to Commercial Alternatives
Commercial Polymarket bot products generally charge either a flat subscription (roughly $29–$50/month for signal or copy-trading features from various providers) or a one-time license fee in a similar range, and several are custodial by design. Self-hosting this template has no subscription or license cost at all — only your Railway infrastructure bill — and keeps custody of your funds and your private key on infrastructure you run yourself throughout.
Getting Started
Deploy the template with DRY_RUN=true (the default) first, and provide your real PRIVATE_KEY, PROFILE_ADDRESS, and COPY_TRADERS. Check the deploy logs: a clean run polls silently; a misconfigured one now logs a clear [startup] or [config] error and idles instead of crashing. Once you've confirmed the logs look right, set DRY_RUN=false to let it place real orders.
Frequently Asked Questions
Why did the reference template end up with poor health?
A code bug: any startup failure other than a ConfigError — most commonly an invalid PRIVATE_KEY — crashes the process uncaught instead of failing gracefully. This template patches that specific handler.
Is this template custodial?
No. You provide your own private key directly to your own deployment; nothing routes through a third-party operator or holds your funds.
What happens if I deploy without filling in real credentials?
The bot logs a clear [config] or [startup] error explaining exactly what's wrong, then idles — it no longer crashes the deployment. Fix the variable and redeploy.
Does this bot need a database?
No — just the one persistent volume at /data for its local state file.
Is DRY_RUN safe to leave on indefinitely?
Yes. With DRY_RUN=true, the bot simulates orders without ever sending them, which is a reasonable way to confirm your trader list and limits look right before risking real funds.
Template Content
polymarket-bot
shruti060701/polymarket-bot-railwayPRIVATE_KEY
Magic EOA private key used to sign orders. Get it from reveal.magic.link/polymarket.
COPY_TRADERS
Comma-separated trader addresses to copy.
PROFILE_ADDRESS
Polymarket profile/proxy address visible in your account.
