Welcome to Agents Academy
Module 01 · Foundations · ~12 min
Infrastructure.
By the end of this module, you’ll have a real address on the internet that an LLM trading agent can call home, somewhere it can run, restart, and remember every decision it made before it crashed.
Before any agent code, before any dashboard, you need a service running on the internet with a URL you can hit from your phone. This module ships a hello-world FastAPI app to Railway, sets the env vars and the persistent volume the rest of the curriculum will use, and ends with you opening the URL on your phone. Twenty-five minutes; nothing yet trades, but everything from here on assumes this floor.
Why should you deploy infrastructure before building a trading agent?
Because retrofitting hosting around agent code that assumes localhost turns into a panicky last-mile sprint with real money on the table; deploying an empty service first makes the platform mistakes cheap. A hello-world teaches the same Procfile, env vars, volume mount, and domain wiring as a full agent, and if you break it you have broken five lines of FastAPI, not your trading loop. The module ships a two-endpoint service (/ returns JSON, /health answers the health check) to Railway, sets production env vars via the CLI, mounts a persistent volume at /app/data addressed through ACADEMY_DATA_DIR, and ends with you opening your domain on your phone. The floor is four things: a URL, env-var secrets, storage that survives redeploys, and tailable logs; every later module assumes it exists.
Platform commands and pricing are illustrative.
Section 01
Why deploy first.
The classic order, build the agent on your laptop, then figure out hosting at the end, sounds reasonable and ages badly. By the time the agent works locally, the platform decisions, the env-var habits, and the persistent-storage path are all things you’re trying to retrofit around code that already assumes localhost. The result is a panicky last-mile sprint with real money on the table.
Deploying empty is the cheapest way to learn the platform. A hello-world that returns a JSON object teaches the same Procfile, the same env vars, the same volume mount, and the same domain wiring as a full agent, and if you break it, you’ve broken five lines of FastAPI, not your trading loop. By the end of this module you have infrastructure; in Module 02 you put a dashboard on it; in later modules every “wire to your dashboard” section assumes both.
What “the floor” means in this curriculum
- A URL. Reachable from your phone, your laptop, and a coach if you have one. Not localhost.
- Production env vars. Set by CLI or UI, never in the repo. The deployed process reads them on boot.
- Persistent storage. A path that survives redeploys, for SQLite, for the NDJSON log, for any state the agent needs.
- Tailable logs. A single command tails the running process. You can read stdout from anywhere with internet.
Section 02
Pick your platform.
The curriculum picks Railway as the default because it gets you to a production URL with persistent storage in about ten minutes, with a free trial that’s long enough to finish the first three modules. The patterns, Procfile, env vars, volume mount, domain, carry over to Fly, Render, or a $5 VPS. Pick whichever matches how you want to learn; the rest of this module uses Railway commands.
Railway
Curriculum defaultWhy: CLI-first deploy, generous free trial, built-in persistent volumes, env-var UI + CLI, log streaming, custom domains in one command, nixpacks autodetects Python and Node.
Watch: trial ends; budget around $5–$10/mo for the dashboard + agent combined past that.
Fly.io
Why: region-aware deploy, slightly cheaper at idle, similar Procfile-style pattern via fly.toml.
Watch: persistent volumes are per-region, so multi-region is more involved than Railway.
Render
Why: friendly UI, good free tier for static + small services, native cron.
Watch: free tier sleeps; not ideal for an agent that needs to wake on a schedule.
$5 VPS
Why: Hetzner / DO / Vultr give you a flat $5–$6/mo bill and a real Linux box. Best long-term economics.
Watch: you’re the platform, systemd, certbot, ufw, log rotation. Worth it once you’ve done the managed path first.
Section 03
The hello-world service.
One file, two endpoints. / returns a JSON object so you have something to look at on your phone; /health returns 200 so Railway’s health-check passes. Both languages share the same Procfile. Pick the tab that matches the runtime you installed in Setup.
// app.ts: minimal hello-world for Railway
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
const app = new Hono();
app.get('/', (c) => {
return c.json({
service: 'agents-academy-deploy',
module: 'Module 01, Infrastructure',
msg: 'You are reading this from a server you deployed.',
ts: new Date().toISOString(),
});
});
app.get('/health', (c) => c.text('ok'));
const port = Number(process.env.PORT) || 8080;
serve({ fetch: app.fetch, port });
console.log(`listening on :${port}`);
# app.py: minimal hello-world for Railway
import os
from datetime import datetime, timezone
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {
"service": "agents-academy-deploy",
"module": "Module 01: Infrastructure",
"msg": "You are reading this from a server you deployed.",
"ts": datetime.now(timezone.utc).isoformat(),
}
@app.get("/health")
def health():
return "ok"
if __name__ == "__main__":
import uvicorn
port = int(os.environ.get("PORT", 8080))
uvicorn.run(app, host="0.0.0.0", port=port)
Procfile
One line. Railway reads Procfile to know how to start the service. The same Procfile pattern works on Heroku, Fly, and a VPS via honcho.
# Procfile (TypeScript)
web: node --env-file=.env app.ts
# Procfile (Python)
web: uvicorn app:app --host 0.0.0.0 --port $PORT
requirements / package files
Pinned versions. Railway’s nixpacks builder reads these to install dependencies. Don’t skip the pin, floating versions are how supply-chain compromises slip in.
# requirements.txt
fastapi==0.115.0
uvicorn[standard]==0.30.6
# package.json (TS): npm i hono @hono/node-server
{ "dependencies": {
"hono": "4.6.5",
"@hono/node-server": "1.13.2"
} }
Section 04
Deploy it.
Railway’s CLI is the fastest path to a URL. Five commands, in order. The first one is interactive (a browser tab opens), everything after that runs unattended.
If railway login hangs
In a sandboxed terminal, the browser tab won’t open automatically. Use railway login --browserless, it prints a URL and a code to paste.
From your project root
# 1. Install + log in
npm i -g @railway/cli # or: brew install railway
railway login # opens a browser tab
# 2. Create or link a project
railway init # name it "agents-academy-deploy"
# 3. Deploy
railway up # uploads, builds, runs
# 4. Set env vars (one per line, repeat as needed)
railway variables --set "ANTHROPIC_API_KEY=sk-ant-…"
railway variables --set "PANEL_TOKEN=$(openssl rand -hex 32)"
# 5. Generate a public domain
railway domain # prints something like agents-academy-deploy.up.railway.app
After step 5, hit the URL in your browser. You should see the JSON from /. If you don’t, the next section’s log-tailing command will tell you why.
Section 05
Persistent storage.
Every redeploy gives you a fresh container with a fresh filesystem. Anything written to disk during one deploy is gone after the next push. The fix is a volume, a chunk of storage that survives redeploys, mounted at a known path. Module 02 puts the dashboard’s seed data here; Module 12 puts the agent’s NDJSON log here; Module 06 puts the SQLite memory file here. Wire it once, in Module 01.
Mount a volume at /app/data
# From the Railway dashboard: Service → Volumes → New
# Mount path: /app/data
# Size: 1 GB (more than enough for the curriculum)
# Or via CLI (newer versions):
railway volume add --mount-path /app/data --size 1
# Tell your code where to write. Set this once and use it everywhere.
railway variables --set "ACADEMY_DATA_DIR=/app/data"
# Verify the volume survives a redeploy:
railway run "echo first-deploy > /app/data/touch.txt"
railway up # redeploy
railway run "cat /app/data/touch.txt" # should print: first-deploy
Reading the path in code
# Python
import os
DATA_DIR = os.environ.get("ACADEMY_DATA_DIR", "./data")
LOG_PATH = os.path.join(DATA_DIR, "agent.log.ndjson")
# TypeScript
const DATA_DIR =
process.env.ACADEMY_DATA_DIR ?? './data';
const LOG_PATH =
`${DATA_DIR}/agent.log.ndjson`;
The fallback to ./data means the same code runs locally without the env var set. Module 02 follows this pattern for the dashboard’s mock data.
Don’t put these on the volume
- –Secrets. Env vars are encrypted; volumes are not. Keys go in env, never on disk.
- –Anything you can rebuild from source. Build artefacts, vendored deps, the volume is for state, not code.
- –Long-term audit logs. 1 GB fills fast under verbose logging. Rotate or stream off-host.
Section 06
Open it on your phone.
This is the part that matters. The hello-world is doing nothing impressive, it returns a JSON object, but it’s doing it from a server you deployed, behind a domain you bound, reachable from a device you didn’t configure. The “a thing I made is on the actual internet” moment is the entire point of moving infrastructure first.
Pull out your phone. Type the domain Railway printed. You should see the JSON. If you don’t, the log will tell you why, the next code block tails the running process so you can read stdout from anywhere.
Tail logs from anywhere
# Live tail: Ctrl+C to exit
railway logs
# Last 100 lines (no follow)
railway logs --tail 100
Mockup, yours will show your timestamp.
Agent infrastructure: what people ask
Each answer also ships invisibly as schema.org FAQ data for search engines and AI assistants. Tap a question to expand.
-
What platform should you deploy a trading agent on?
The curriculum default is Railway: CLI-first deploys, a free trial long enough for the first three modules, built-in persistent volumes, log streaming, and custom domains in one command, with nixpacks autodetecting Python and Node. The same Procfile, env-var, and volume patterns carry over to Fly.io, Render, or a $5 VPS; the module budgets roughly $5–$10/mo for the dashboard plus agent once the trial ends. -
What does the hello-world service for a trading agent look like?
One file, two endpoints:/returns a JSON object so you have something to look at on your phone, and/healthreturns200so the platform health check passes. Bind to0.0.0.0, readPORTfrom the environment, and start it from a one-line Procfile such asweb: uvicorn app:app --host 0.0.0.0 --port $PORT. Pin dependency versions; floating versions are how supply-chain compromises slip in. -
How do you give an agent storage that survives redeploys?
Mount a volume at/app/data(1 GB covers the whole curriculum) and point your code at it through one env var,ACADEMY_DATA_DIR=/app/data, with a./datafallback so the same code runs locally. Verify it by writing a file, redeploying, and reading it back. Module 02 puts the dashboard’s seed data there, Module 06 the SQLite memory file, Module 12 the agent’s NDJSON log. -
Should secrets ever live on the persistent volume?
No. Env vars are encrypted; volumes are not, so keys go in env vars set via the platform’s CLI or UI, never in the repo, the image, or a log. The volume is for state, not code: skip build artefacts you can rebuild from source, and rotate or stream long-term audit logs off-host, because 1 GB fills fast under verbose logging. -
How do you debug a deployed agent service?
Tail the logs from anywhere:railway logslive-tails the running process, andrailway logs --tail 100prints the last 100 lines without following. Tailable logs are part of the infrastructure floor; if your new domain does not show the JSON on your phone, stdout tells you why. And ifrailway loginhangs in a sandboxed terminal,railway login --browserlessprints a URL and a code to paste.
Section 07
Module checklist.
Every box checked means you have running infrastructure. Module 02 will deploy a dashboard on top of it; if any of these fail, fix them now, everything downstream assumes the floor.
Hello-world service runs locally on localhost:8080 and returns JSON at /.
A Procfile exists and the start command binds to 0.0.0.0 on $PORT.
Railway CLI installed; railway login + railway init done.
First deploy via railway up succeeded; the build logs show no errors.
At least one env var (e.g. PANEL_TOKEN) set via railway variables --set and visible in railway variables.
Persistent volume mounted at /app/data; ACADEMY_DATA_DIR set; survives a redeploy.
Production URL generated via railway domain and bookmarked.
Opened the URL on your phone; saw the JSON; tailed logs once via railway logs.
Module 01 complete
On the air.
Your agent has an address now. When it starts thinking and trading, it has somewhere to live that doesn’t disappear when you close your laptop, and a memory that survives every redeploy, so you never have to re-prompt it from scratch.
Concretely, you shipped a service to the internet, set production env vars without committing them, mounted a volume that survives redeploys, and hit your domain from a device you didn’t configure. The hello-world is a placeholder; the floor underneath it is the real artefact.
A URL beats localhost for every kind of feedback, debugging, sharing, intervening from your phone. Deploy first, build second.
Secrets live in env vars, set via the platform’s CLI or UI. Never in the repo, never in the image, never in a log.
Persistent state has one home: a volume mounted at /app/data, addressed via ACADEMY_DATA_DIR. Every later module assumes both.
Next up: Module 02 builds two dashboards on top of this infrastructure, a one-page HTML panel and a Telegram bot, both running on seed data so you see your first “trade” on the dashboard before the agent even exists. The visceral moment.
Complete the checklist above to unlock