Deployment steps adapted to this server's exact stack — no wasted installs.
| Component | Guide Said | This Server | Action |
|---|---|---|---|
| Python | 3.11 |
3.12.3 ADJUSTED |
Use python3.12 -m venv — no install needed |
| PostgreSQL | 15 |
16.13 ✓ RUNNING |
Skip install — use existing PG16 |
| Redis | Install if missing | Running ✓ PONG | Skip install — already live |
| Node.js | 20 |
22.21.1 ✓ INSTALLED |
Skip install — v22 is compatible |
| Web server | OLS or Nginx | OLS only → Option A | Use CyberPanel Reverse Proxy UI |
| Uvicorn workers | 1 or 2 | 4 CPUs, 15 GB RAM → 2 workers | --workers 2 |
| DATABASE_URL | postgresql+asyncpg://...5432/csr2mods |
Same format PG16 | No URL change needed — asyncpg works with PG16 |
Nothing to install for Python, Node.js, PostgreSQL, or Redis — all present and running.
asyncpg driver and Alembic work fine with PG16. No changes to your app code are needed, but do not run apt install postgresql-15.
You may need these system libs for Python packages that compile C extensions:
apt install -y libpq-dev build-essential python3.12-dev python3.12-venv
PostgreSQL 16 and Redis are running. Create the csr2mods user + database using the repo script.
# Run the repo's DB setup script — targets the local PG16 instance DB_PASSWORD="CHOOSE_A_STRONG_PASSWORD" bash /path/to/repo/backend/scripts/setup_db.sh # Verify the DB was created: sudo -u postgres psql -c "\l" | grep csr2 # Redis is already configured and running — nothing to do: redis-cli ping # should return: PONG
setup_db.sh uses a hardcoded socket path for PG15 (/var/run/postgresql/15-main), edit it to /var/run/postgresql or just connect via localhost.
Repo not yet present on the server. Clone it under the domain home.
cd /home/store.csr2racer.com git clone https://github.com/YOUR_USERNAME/csr2-store-clone.git cd csr2-store-clone
Use python3.12 — Python 3.11 is not installed and we're not installing it.
cd /home/store.csr2racer.com/csr2-store-clone/backend # Use python3.12 (NOT python3.11 — it's not installed) python3.12 -m venv venv source venv/bin/activate # Verify: python --version # should show: Python 3.12.x pip install --upgrade pip pip install -r requirements.txt
.env on the ServerNever commit this file. Fill in real values before starting the service.
cat > /home/store.csr2racer.com/csr2-store-clone/backend/.env << 'EOF' # PostgreSQL 16 — asyncpg driver (works with PG16 unchanged) DATABASE_URL=postgresql+asyncpg://csr2mods:YOUR_DB_PASSWORD@localhost:5432/csr2mods SECRET_KEY=REPLACE_WITH_GENERATED_KEY ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=1440 REDIS_URL=redis://localhost:6379 STRIPE_SECRET_KEY=sk_live_placeholder STRIPE_WEBHOOK_SECRET=whsec_placeholder RESEND_API_KEY=re_placeholder FROM_EMAIL=noreply@store.csr2racer.com APP_ENV=production FRONTEND_URL=https://store.csr2racer.com EOF # Generate SECRET_KEY — paste output into the .env above: openssl rand -hex 32
Apply Alembic schema then seed the initial data.
cd /home/store.csr2racer.com/csr2-store-clone/backend
source venv/bin/activate
alembic upgrade head
python scripts/seed.py
# Change the insecure default admin password before going live!
4 CPUs + 15 GB RAM → run 2 uvicorn workers. Edit the service file paths before copying.
# If the service file hardcodes /home/csr2mods/ paths, update them first: sed -i 's|/home/csr2mods/csr2-store-clone|/home/store.csr2racer.com/csr2-store-clone|g' \ /home/store.csr2racer.com/csr2-store-clone/backend/scripts/csr2mods-api.service # Make sure the workers flag is set to 2 — 4 CPUs justifies it: grep -i workers /home/store.csr2racer.com/csr2-store-clone/backend/scripts/csr2mods-api.service cp /home/store.csr2racer.com/csr2-store-clone/backend/scripts/csr2mods-api.service \ /etc/systemd/system/ systemctl daemon-reload systemctl enable csr2mods-api systemctl start csr2mods-api systemctl status csr2mods-api --no-pager # Health check — must return {"status":"ok","version":"2.0.0"}: curl http://127.0.0.1:8001/health
No Nginx on this server. Use CyberPanel's built-in reverse proxy UI for store.csr2racer.com.
store.csr2racer.com/apihttp://127.0.0.1:8001context /api {
type proxy
handler 127.0.0.1:8001
addDefaultCharset off
}
rewrite {
enable 1
rules <<<END_rules
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
END_rules
}
Issue Let's Encrypt via CyberPanel — same flow used for all other domains on this server.
store.csr2racer.com → SSL → Issue Let's Encryptcurl -I https://store.csr2racer.com/api/healthThe frontend/ directory doesn't exist yet. Node.js 22 is already installed and ready.
npm run build → sync dist/ to /home/store.csr2racer.com/public_html/
| # | Item | Notes |
|---|---|---|
| 1 | Server summary | Ubuntu 24.04 · 15 GB RAM · 4 vCPU · OLS · PG16 · Redis ✓ |
| 2 | What was installed | Nothing new — all runtimes pre-existing |
| 3 | Deployment approach | Option A (OLS only) · Option C (2 workers) |
| 4 | FastAPI port | 127.0.0.1:8001 |
| 5 | Repo location | /home/store.csr2racer.com/csr2-store-clone |
| 6 | Health check | curl http://127.0.0.1:8001/health → {"status":"ok","version":"2.0.0"} |
| 7 | Issues encountered | — |
| 8 | Remaining steps | Real Stripe/Resend keys · Admin password · Frontend (Plan 3) |
store.csr2racer.com.backend/.env on the server.CHANGE_ADMIN_PASSWORD_BEFORE_DEPLOY in seed.py is insecure..env before going live.SECRET_KEY with openssl rand -hex 32. Never reuse keys across environments. Never commit .env.