One-Person Company Infrastructure: The $350/Year Stack Behind a Six-Figure Solo Business
The Pain — You assume a one-person company 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 solo business 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 one-person company 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 (one-person company) — 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 solo business 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 one-person company 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 {
include /etc/nginx/mime.types;
default_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;
include /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 one-person company'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 (0)