Real Business Scenario Infrastructure: The $350/Year Stack Behind a Six-Figure Production System
The Pain — You assume a real business scenario needs expensive servers, domains, OSS buckets, and databases to start — and that picking the "right" cloud-native stack is the real engineering challenge.
What You'll Learn:
- The real infrastructure bill behind a production system earning ¥500K/year (~$70K) — ¥2,500/year total (~$350)
- The "iron triangle" stack (FastAPI + Celery + SQLAlchemy on a single VPS) and why one box beats microservices
- Production Nginx configuration: SSL, rate limiting, WebSocket upgrade, static caching
- Docker Compose + Portainer deployment, a GitHub Actions CI/CD pipeline, and a ¥0 monitoring trio
- A simple rule for knowing when to scale — and when to leave your infrastructure alone
Last week a reader messaged me: "Wu, I want to run a real business scenario too, but a server alone runs into the thousands — then a domain, OSS, a database... isn't the startup cost huge?"
My reply was blunt: your biggest cost isn't the server. It's the time you burn on the wrong tech stack.
I've been running MoLi AGI — an OPC (real business scenario) — for nearly six months now. On top of it I've built a complete automated business: daily WeChat publishing, an AI Agent service, SaaS mini-tools, and a data dashboard — all in production. Today I'm laying out the entire stack, real bills included, so you can see exactly what infrastructure a ¥500K-a-year production system actually needs.

The Iron Triangle: Compute, Data, Deliver — ~$350/year
0. Environment Dependencies
Before we start, here's the core dependency list running on my server.
Python environment (requirements.txt):
fastapi==0.115.0
uvicorn[standard]==0.30.0
gunicorn==22.0.0
celery[redis]==5.4.0
sqlalchemy==2.0.35
pymysql==1.1.1
redis==5.2.0
minio==7.2.10
boto3==1.35.0
python-dotenv==1.0.1
httpx==0.27.0
System dependencies (Docker environment):
# Lightweight server with Docker + Docker Compose installed
docker --version # >= 24.0
docker compose version # >= 2.20
# One-line install script (Ubuntu 22.04/24.04)
curl -fsSL https://get.docker.com | bash
apt-get install -y docker-compose-plugin
Verify everything is ready:
# Verify Python packages
python -c "import fastapi, celery, sqlalchemy, redis, minio; print('OK')"
# Verify Docker service
docker run hello-world
# Verify ports are not occupied
ss -tlnp | grep -E '8000|443|80|3306|6379|9000'
1. The Tech Stack Panorama: My "Iron Triangle"
Here's the whole architecture. What does a real business scenario fear most? Going big and broad. You cannot manage a Kubernetes cluster, write business code, and run ops monitoring all by yourself. The architecture must be minimal, maintainable, and operable by a single person.
┌───────────────────────────────────────────────────────┐
│ User Traffic Entry │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ WeChat/H5 │ │ MiniApp │ │ API GW │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
└────────┼─────────────┼─────────────┼──────────────────┘
│ │ │ │ │
┌────────▼─────────────▼─────────────▼──────────────────┐
│ Business Layer (single process) │
│ ┌───────────────────────────────────────┐ │
│ │ FastAPI + Celery + SQLAlchemy │ │
│ │ One process: API / workers / cron │ │
│ └───────────────────┬───────────────────┘ │
└───────────────────────────┼───────────────────────────┘
│ │ │
┌───────────────────────────▼───────────────────────────┐
│ Data & Infrastructure Layer │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────────┐ │
│ │ MySQL │ │ Redis │ │ MinIO │ │ Aliyun OSS │ │
│ └────────┘ └────────┘ └────────┘ └────────────┘ │
│ ┌────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ ES │ │ GitHub/Gitee │ │ Docker Swarm │ │
│ └────────┘ └──────────────┘ └──────────────┘ │
└───────────────────────────────────────────────────────┘
My core principles are only three:
- Merge, don't split — if a single VPS can run it, don't reach for microservices.
- SaaS over self-hosted — push notifications, email, monitoring: use third parties whenever possible.
- Containerize everything — the Docker toolchain makes migration cost close to zero.
2. Server Choice: A Lightweight VPS Is the Sweet Spot for an OPC
Let's answer the title question directly: what server does a ¥500K/year business need?
One 2-core 4GB lightweight application server, under ¥100/month.
I currently run Alibaba Cloud's "Lightweight Application Server" on the Hong Kong node: 2 cores, 4GB RAM, 40GB SSD, 3Mbps bandwidth — about ¥84/month on annual billing. What runs on it?
- Main API (FastAPI + Gunicorn + Uvicorn)
- Celery Worker + Beat (scheduled jobs)
- Redis (cache + message queue)
- MySQL 8.0 (dev environment only — RDS lives elsewhere)
- MinIO (object storage)
- Nginx (reverse proxy + SSL)
- Portainer (container management panel)
One machine, seven services, all Dockerized. Peak memory usage: 3.2GB. Average CPU load: 0.8. And there's still headroom for two more small services.
Why not the full cloud-native stack? I tried it. In the first week on EKS I burned ¥200 on the NAT Gateway alone — with 37 daily active users.
The golden rule of OPC: don't touch Kubernetes before you have 10,000 users.
For a single node, Docker Compose + Portainer is more than enough. If you ever need to scale horizontally, Swarm mode makes the migration trivial — change the deploy block in compose.yml and you go from single box to cluster.
Here's the core of my docker-compose.yml:
version: '3.8'
services:
api:
build: ./app
environment:
- DATABASE_URL=mysql+pymysql://user:***@db:3306/opc
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
volumes:
- ./app:/app
ports:
- "8000:8000"
restart: unless-stopped
celery:
build: ./app
command: celery -A tasks worker -l info
depends_on:
- redis
- db
redis:
image: redis:7-alpine
restart: unless-stopped
db:
image: mysql:8.0
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
restart: unless-stopped
volumes:
mysql_data:
The biggest win of containerization isn't performance — it's the drop in cognitive load. Moving machines is a single docker compose up -d; everything is back up in a minute.
3. Reverse Proxy & SSL: Complete Nginx Configuration
Too many OPC APIs run naked on the public internet: no HTTPS, no rate limiting, no anti-scraping — the front door wide open. Below is the Nginx config I run in production, covering SSL (Let's Encrypt with auto-renewal), rate limiting, WebSocket upgrade, and static asset caching.
3.1 Main Nginx config
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
in clude /etc/nginx/mime.types;
def ault_type application/octet-stream;
server_tokens off; # hide the version number
# log format (includes request time — handy for hunting slow endpoints)
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
# Gzip compression
gzip on;
gzip_min_length 1k;
gzip_types text/plain application/json application/javascript text/css;
# Rate limit zone: max 30 requests/sec per IP (API protection)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s;
# Connection limit: max 20 concurrent connections per IP
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
in clude /etc/nginx/conf.d/*.conf;
}
3.2 Site config (with SSL)
# /etc/nginx/conf.d/opc.conf
# force HTTP → HTTPS redirect
server {
listen 80;
server_name api.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
# === SSL certificate (Let's Encrypt) ===
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
# === SSL hardening ===
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# === Security headers ===
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=63072000" always;
# === Rate limiting (protect the backend) ===
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 20;
# max client request body
client_max_body_size 10m;
# === Static assets (served by Nginx directly, no backend round-trip) ===
location /static/ {
alias /opt/opc/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# === API reverse proxy ===
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# timeouts
proxy_connect_timeout 30s;
proxy_read_timeout 60s;
proxy_send_timeout 30s;
# buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
}
# === WebSocket upgrade (needed for long-lived AI Agent connections) ===
location /ws/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
}
}
3.3 SSL certificate auto-renewal
# install certbot
apt-get install -y certbot python3-certbot-nginx
# first issuance (make sure the domain resolves to your server first)
certbot --nginx -d api.yourdomain.com -d www.yourdomain.com
# cron auto-renewal (1st of every month at 3am)
echo "0 3 1 * * certbot renew --quiet --post-hook 'nginx -s reload'" | crontab -
# verify auto-renewal (dry run)
certbot renew --dry-run
3.4 Verifying Nginx + SSL
# 1. check config syntax
nginx -t
# 2. reload config
nginx -s reload
# 3. test HTTPS access
curl -I https://api.yourdomain.com
# 4. check SSL certificate validity
echo | openssl s_client -servername api.yourdomain.com -connect api.yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -dates
# 5. SSL Labs online check (aim for an A+ rating)
# open in browser: https://www.ssllabs.com/ssltest/analyze.html?d=api.yourdomain.com
4. Database & Storage: Layering Is Just a Way to Save Money
4.1 Relational database
Primary: Alibaba Cloud RDS MySQL, 2 cores 4GB, 50GB SSD — ¥98/month (pay-as-you-go, no traffic fee).
Why not self-host MySQL? Because self-hosting means writing backup scripts, expanding the disk yourself, and recovering from crashes yourself. An OPC's time is income — spending it on ops is worse than spending it on code. RDS gives you automatic backups, one-click rollback, and slow-query logs. A 15-minute look every six months is all it takes.
4.2 Cache layer
Redis 7-alpine runs directly on the lightweight server — the Docker image is only 32MB. In production I gave it a 512MB memory limit; it actually uses under 100MB.
Use cases:
- Session storage
- Celery result backend
- API response cache (TTL 60s, ~65% hit rate)
- Simple counters (DAU, API call volume)
4.3 File storage
The object storage trio: OSS (primary) + MinIO (local cache) + CDN.
Alibaba Cloud OSS (standard tier): 50GB capacity + 10GB monthly traffic ≈ ¥15/month. What goes in it?
- User-uploaded images and PDFs
- Generated article cover images
- Intermediate files produced by AI Agents
MinIO acts as a local hot cache — frequently accessed files are pulled from OSS to the local box to cut egress fees. A simple LRU eviction strategy:
import os
from pathlib import Path
from functools import lru_cache
from minio import Minio # <- fix: import the MinIO client
class FileCache:
def __init__(
self,
local_dir: str = "/data/cache",
max_size_mb: int = 1024,
# fix: initialize the MinIO client (missing in the original draft,
# which left self.client undefined)
oss_endpoint: str = "oss-cn-hongkong.aliyuncs.com",
oss_access_key: str = "",
oss_secret_key: str = "",
oss_bucket: str = "opc-files",
):
self.local_dir = Path(local_dir)
self.local_dir.mkdir(parents=True, exist_ok=True)
self.max_size = max_size_mb * 1024 * 1024
# ★ Bug fix: the original code never initialized the MinIO client
self.client = Minio(
oss_endpoint,
access_key=oss_access_key,
secret_key=oss_secret_key,
secure=True,
)
self.oss_bucket = oss_bucket
@lru_cache(maxsize=128)
def get(self, key: str) -> bytes:
local_path = self.local_dir / key
if local_path.exists():
return local_path.read_bytes()
# pull from OSS (self.client is now properly defined)
data = self.client.get_object(self.oss_bucket, key).read()
local_path.write_bytes(data)
self._evict_if_needed()
return data
def _evict_if_needed(self):
total = sum(
f.stat().st_size for f in self.local_dir.rglob("*") if f.is_file()
)
if total > self.max_size:
files = sorted(self.local_dir.rglob("*"), key=os.path.getatime)
for f in files[:10]:
f.unlink()
Bug note: the original draft called
self.client.get_object(...)without ever initializingself.client, which raisedAttributeError: 'FileCache' object has no attribute 'client'at runtime. Fixed above.
CDN origin-pull traffic costs about ¥3–5 per month. Absurdly cheap.
5. CI/CD & Deployment: GitHub Actions Instead of Jenkins
One person does not need Jenkins. The GitHub Actions free tier is more than enough for an OPC — I can't use it all up.
My deployment pipeline:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: |
docker compose -f docker-compose.test.yml up --abort-on-container-exit
- name: Build and push
env:
DOCKER_BUILDKIT: 1
run: |
docker build -t opc-app:${{ github.sha }} .
docker save opc-app:${{ github.sha }} | gzip > app.tar.gz
- name: Deploy to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_KEY }}
source: "app.tar.gz"
target: "/opt/opc/deploy/"
- name: Restart services
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_KEY }}
script: |
cd /opt/opc
docker compose pull
docker compose up -d --force-recreate api
docker image prune -f
The whole flow: git push → GitHub Actions runs tests → builds the image → SCPs it to the server → docker compose hot-reloads. From push to live: 47 seconds on average. Nobody needs to watch it.
6. What About Monitoring?
Three tools, all free:
| Tool | Purpose | Free tier |
|---|---|---|
| Sentry | Error tracking | 5,000 events/month — more than a solo project can use |
| UptimeRobot | Availability monitoring | 5 URLs free, probed every 5 minutes; email + SMS alerts on downtime |
| Healthchecks.io | Cron job monitoring | Celery Beat pings after every run; alert if the ping times out |
Combined monthly cost of all three: ¥0.
7. The Annual Bill: Every Yuan Disclosed
A lot of people assume building a tech product must burn money. Here's my actual bill (annual pricing, CNY):
| Item | Spec | Year | Month |
|---|---|---|---|
| Lightweight server | 2C4G, 40GB SSD | ¥1,008 | ¥84 |
| RDS MySQL | 2C4G, 50GB SSD | ¥1,176 | ¥98 |
| Alibaba Cloud OSS | 50GB + CDN traffic | ¥180 | ¥15 |
| Domain | 1 × .com + 1 × .cn | ¥136 | ¥11 |
| Enterprise email | Alibaba free tier | ¥0 | ¥0 |
| SSL certificate | Let's Encrypt (auto-renew) | ¥0 | ¥0 |
| GitHub Pro | Personal | ¥0 | ¥0 |
| Sentry | Free tier | ¥0 | ¥0 |
| Monitoring tools | UptimeRobot + Healthchecks | ¥0 | ¥0 |
| Total | ¥2,500 | ¥208 |
¥2,500 a year (~$350). That is the entire infrastructure cost of running MoLi AGI.
For a brand-new OPC, you can go even cheaper:
- Squeeze every service onto one lightweight server (¥1,008/year is enough)
- Replace MySQL with SQLite (¥0)
- Self-host object storage with MinIO (mind the disk filling up)
Extreme minimum: ¥1,008/year — ¥84/month.
8. Why I Chose the "Old-School" Stack
You may have noticed: no Serverless (Lambda/FaaS), no NoSQL (MongoDB is fine — I just don't need it), no message queue (beyond Redis's built-in pub/sub).
The reason is simple: a real business scenario's stack must serve "fast delivery," not "architectural aesthetics."
Can this stack survive growth from 1,000 to 10,000 users?
- API: add an Nginx cache layer
- Database: add a read replica
- Server: upgrade to 4 cores 8GB
How long does that take? One weekend. And before that weekend ever arrives, I've already run a full year at ¥2,500 and earned more than 10× that back.
OPC tech selection is not an engineering problem — it's a finance problem. Is every minute of server time making you money, or just satisfying your "technical hygiene"?
When should you upgrade? Here's a simple decision rule:
- If server cost is below 1% of your monthly income, don't touch the infrastructure.
- If ops time exceeds coding time, upgrade immediately.
I tested this rule for three months: ops work averaged under 30 minutes per week (mostly glancing at monitoring dashboards and deploying), and server cost came to 0.2% of monthly income. So far, nothing has moved.
9. Action Checklist
If you want to start an OPC too, here's what you can do today:
- Buy a lightweight server (Alibaba Cloud / Tencent Cloud / Huawei Cloud — whichever is cheapest), 2C2G minimum, ¥600–800/year
- Register a .com domain (~¥55/year)
- Install Docker + Docker Compose (5 minutes)
- Manage code on GitHub and configure a GitHub Actions auto-deploy
- Deploy your first API — even a Hello World — and complete the push-to-live loop
- Grab my Docker Compose starter template: github.com/weiwuji/opc-starter
10. Verification Checklist (Run After Deployment)
Spend 5 minutes running through this to make sure the whole chain is wired up:
# === 1. Base service health ===
docker compose ps # all containers STATUS=Up
docker compose logs --tail=20 api # no abnormal logs in API
curl http://localhost:8000/health # expect {"status":"ok"}
# === 2. Database connectivity ===
docker compose exec db mysqladmin ping -h 127.0.0.1 -u root -p${DB_PASSWORD}
# expected: mysqld is alive
# === 3. Redis connectivity ===
docker compose exec redis redis-cli ping
# expected: PONG
# === 4. Celery worker ===
docker compose exec celery celery -A tasks inspect ping
# expected: {'celery@xxx': {'ok': 'pong'}}
# === 5. Nginx + HTTPS ===
nginx -t && nginx -s reload
curl -o /dev/null -s -w "%{http_code}" https://yourdomain.com
# expected: 200
# === 6. SSL certificate validity ===
echo | openssl s_client -servername yourdomain.com \
-connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -dates
# check that the notAfter date is still in the future
# === 7. GitHub Actions pipeline ===
# In the GitHub repo → Actions → trigger the workflow manually
# confirm all four steps are green: test → build → deploy → restart
# === 8. File cache (FileCache) ===
python -c "
from minio import Minio
fc = FileCache(oss_access_key='test', oss_secret_key='test')
print('FileCache initialized OK:', fc.local_dir)
"
# expected: FileCache initialized OK: /data/cache
If everything passes, your OPC infrastructure is production-ready.
In the next article I'll walk you through building the AI automation pipeline — from WeChat content generation, to AI Agent auto-replies, to automatic data rollups into a Notion dashboard. Full-chain automation. One person, the output of ten.
As I wrote in my private notes: "While others are still agonizing over whether their server is big enough, Wu Ji has already closed the loop on a thousand-yuan budget. Not because the tech is fancy — because he knows what to save on and what not to."
See you in the next one.
About the author: Wu Ji (无记) — AI & digitalization practitioner focused on Agent engineering, Loop Engineering, and digital transformation. Practical, hands-on tutorials — follow along and it just works.

Top comments (1)
Thanks for mentioning us. And by the way, if you ever build something using UptimeRobot we would love to learn more about it.
We have a new series where we highlight our members. If you've built something with UptimeRobot, for example if MoLi AGI is actively using it, we would love to see it and have a short, async discussion with you.