Welcome to Limitless Trader Lab
Day 2 of 7 · Cohort intensive · 60 minutes
Deploy infrastructure.
Before any bot code, before any control panel, you need a service running on the internet with a URL you can hit from your phone. Today: deploy a hello-world FastAPI to Railway, mount a persistent volume, set env vars via CLI, and bind a public domain. Sixty minutes. Nothing trades yet. Tomorrow’s panel lives on top of this.
Today you’ll learn
You’ll learn how to deploy a hello-world FastAPI service to Railway, set env vars via the CLI, mount a persistent volume at /app/data, attach a custom domain, and confirm the whole thing works by opening the URL on your phone. This is the substrate the rest of the cohort lives on top of: tomorrow’s panel mounts to the same volume, Day 4’s first programmatic fill writes there, Day 6’s Claude loop runs there.
Section 01
Why deploy first.
Build the bot first and deployment becomes a Day 6 fire-drill, exactly when you have the least slack. So we invert the order: ship the infrastructure today, empty, before there’s a bot to break against it. Today the host exists, the volume exists, the URL exists. Tomorrow you drop a panel onto it. Day 4 the first real fill lands in that panel, on your phone, in seconds. The deploy story is the easy story when you do it before there’s anything to lose. Sixty minutes from now, you have a service answering JSON on the public internet from a URL you can text to a friend.
Off the laptop
Code that needs your laptop on isn’t a bot. The host runs without you, today is when that becomes true.
Persistent volume
Containers reset on every redeploy. The volume at /app/data survives, that’s where logs, fills, and panel state live for the rest of the week.
Phone-friendly URL
Public domain you can hit from anywhere. The receipt is the JSON in a mobile browser tab. Tomorrow’s panel uses the same URL.
Section 02
Set up Railway.
Railway is the host we’ll deploy to today. Think of it as “a place on the internet that runs your code for you.” You hand it a folder of Python files, it builds a container, runs them, and gives you back a public URL. No servers to manage, no SSH, no nginx. The free / hobby tier covers the whole cohort week, you only pay if you scale beyond it later. Create the account now so it’s ready when the code is.
You are now: creating a Railway account
Open railway.com in a new tab and click Login. Sign in with GitHub (recommended) or email.
GitHub sign-in is one click and means you don’t need to remember a separate password. Email works too, you’ll just need to verify the address. Either way, you land on the Railway dashboard, that’s where your project will appear after the CLI deploy in Section 04.
You are now: confirming the free tier
Railway gives every new account a trial credit. No card needed to finish today.
If a card prompt appears, you can dismiss it. Today’s deploy fits inside the trial. We’ll talk about plan limits in Day 7 once you know what your service actually uses.
Section 03
The hello-world app.
A 25-line FastAPI app: one route at / that returns the build time and the data dir, one route at /health for the platform’s health check. No bot logic. Just enough to prove the deploy works end-to-end. Tomorrow you mount the panel on top.
You are now: creating the project folder
Make a fresh folder for this week’s deploy and step into it.
Same folder you’ll use the rest of the week. Day 3 adds the panel files here, Day 4 adds the order scripts, Day 6 wires Claude. Git isn’t required, the cohort doesn’t depend on it. If you have Git installed and want version history, git init in this folder is a one-line add anytime this week. (If git isn’t recognized when you try, install from git-scm.com/downloads; not a Day 2 blocker.)
You are now: writing the hello-world app
Save this as app.py in the new folder. Twenty-five lines.
You also need two tiny companion files in the same limitless-lab/ folder. They’re short, but Railway needs both, the first tells it which Python packages to install, the second tells it how to start your app.
You are now: writing requirements.txt
Create a new file named requirements.txt (exact name, no extension change) in the same folder as app.py. Paste the two lines below and save.
That’s the entire file, two lines, no commas, no quotes, no version pins for the cohort. Railway runs pip install -r requirements.txt on every deploy to install whatever’s listed here.
You are now: writing the Procfile
Create another new file in the same folder named Procfile, exact spelling, capital P, no extension at all (not .txt, not .md). Paste the single line below and save.
One line, no quotes around it. This tells Railway: “run a web process by starting uvicorn against the app object inside app.py, listening on every interface, on whatever port Railway assigns.” If your editor insists on adding .txt, save it anyway then rename in your file explorer to strip the extension, the deploy will fail if the file is named Procfile.txt.
You are now: smoke-testing locally
Make sure it starts on your laptop before you push to Railway.
If you see the JSON, you’re ready to deploy. If you see import errors, paste them into Claude: “Got this error running uvicorn: [paste]. Help me debug.”
Section 04
Five Railway commands.
Railway is the lowest-friction host for a Python service. Five commands and you have a public URL. The first one (railway login) is interactive, the rest run unattended. Free / hobby tier is plenty for the cohort week; verify Railway’s current limits in their dashboard before you commit beyond Day 7.
You are now: installing the Railway CLI
macOS: brew install railway. Anywhere with Node: npm i -g @railway/cli. Windows: download the MSI from docs.railway.com/guides/cli.
You are now: deploying
From inside limitless-lab/:
If railway login hangs in a sandboxed terminal, use railway login --browserless, it prints a URL + code to paste.
You are now: hitting the URL
Open the domain Railway gave you in your laptop browser. You should see the JSON from /, service name, boot timestamp, data dir.
If you see a build error or a 502, run railway logs and paste the last 30 lines into Claude. The fix is usually one of: missing requirements.txt, wrong port binding (must use $PORT), or a typo in the Procfile.
Section 05
Mount the volume.
Every redeploy gives you a fresh container with a fresh filesystem. Anything written during one deploy is gone after the next push. The fix is a volume, a chunk of storage that survives redeploys, mounted at /app/data. Day 3’s panel reads seed JSON from here. Day 4’s first fill writes here. Day 6’s Claude loop logs here.
You are now: provisioning the volume
From the Railway dashboard: Service → Volumes → New. Mount path /app/data. Size 1 GB (more than enough for the cohort). Or via CLI on newer versions:
You are now: proving the volume survives
Touch a file on the volume, redeploy, read it back. If it’s still there, the volume is wired correctly.
If the file is gone, the volume isn’t mounted at /app/data. Recheck the dashboard, mount path matters more than the volume name.
You are now: confirming the env var lands in code
Hit your URL again. The data_dir field in the JSON should now read /app/data (not ./data) and data_dir_writable should be true.
If data_dir_writable is false, the volume isn’t mounted to the path the env var points at. Same fix as above.
Section 06
Open it on your phone.
The deploy isn’t real until you can hit it from a device that isn’t your laptop. Pull out your phone, type the URL, see the JSON. That’s the moment the rest of the cohort cashes in on. The panel tomorrow lands at the same URL; the kill-switch on Day 7 is one tap away from this same address.
You are now: opening the URL on your phone
Same domain Railway gave you. Type it (or text it to yourself). You should see the same JSON your laptop browser shows.
Bookmark it. Pin it to your home screen. From Day 3 onwards, this is the muscle memory: phone in hand, panel in tab, bot in the air.
You are now: screenshotting it
Phone screenshot of the JSON in a mobile browser. Crop to remove anything sensitive. The screenshot is the deliverable.
Section 07
Today’s deliverable.
A phone screenshot of a JSON URL is the cohort’s permanent receipt that you shipped infrastructure before you shipped a bot. Most builders never get past local development. You did, on Day 2 of week 1. Post the proof.
Day 2 · Deliverable
Post in #trader-lab.
Phone screenshot of your deployed URL showing the JSON response (service, data_dir, data_dir_writable: true).
Post it in #trader-lab with a [D2] tag in the caption so the cohort can scan back to today’s posts.
Caption: “[D2] Day 2 done. Host: Railway. Volume mounted at /app/data. URL: [your domain]. Snags: [none / brief].”
Coach pins one or two clean deploys as exemplars. If you got stuck on the $PORT binding or the volume mount, post that too, the cohort learns more from the snags than the wins.
Day 2 complete
Service in the air.
A FastAPI service is running on Railway with a public domain, a persistent volume mounted at /app/data, and env vars set via the CLI. The phone test is the receipt.
A 25-line app.py + requirements.txt + Procfile on your laptop and pushed to a Railway project, the substrate the rest of the week mounts on top of.
A persistent volume at /app/data that survived a redeploy, addressed in code via ACADEMY_DATA_DIR. Day 3 seeds it with panel data; Day 4 writes the first real fill there.
A phone-bookmarkable URL posted in #trader-lab with a [D2] tag, the same address tomorrow’s panel renders to.
Tomorrow: Drop the control panel on top of this infrastructure. Bonus pack drops mid-day, coach pins the link in #trader-lab. Mid-week live Google Meet at the time pinned in your acceptance email: coach demos the panel against seed data, runs an intervention drill (kill switch from phone), and unblocks anyone stuck.
Day 3 unlocks tomorrow at 9am local · bonus pack drops mid-morning · live call mid-week