feat: prepare project for render free tier deployment
Browse files- Dockerfile +4 -2
- Procfile +1 -0
- docs/DEPLOY.md +66 -0
- render.yaml +38 -0
Dockerfile
CHANGED
|
@@ -16,6 +16,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
| 16 |
NLTK_DATA=/app/nltk_data \
|
| 17 |
MODEL_NAME="Meta-Feature Stacking (Production)" \
|
| 18 |
ENV=production \
|
|
|
|
| 19 |
INSTALL_HF=${INSTALL_HF}
|
| 20 |
|
| 21 |
WORKDIR /app
|
|
@@ -50,6 +51,7 @@ COPY .env.example .env.example
|
|
| 50 |
EXPOSE 8000
|
| 51 |
|
| 52 |
HEALTHCHECK --interval=10s --timeout=5s --retries=12 --start-period=60s \
|
| 53 |
-
CMD curl -f http://localhost:8000/health || exit 1
|
| 54 |
|
| 55 |
-
|
|
|
|
|
|
| 16 |
NLTK_DATA=/app/nltk_data \
|
| 17 |
MODEL_NAME="Meta-Feature Stacking (Production)" \
|
| 18 |
ENV=production \
|
| 19 |
+
PORT=8000 \
|
| 20 |
INSTALL_HF=${INSTALL_HF}
|
| 21 |
|
| 22 |
WORKDIR /app
|
|
|
|
| 51 |
EXPOSE 8000
|
| 52 |
|
| 53 |
HEALTHCHECK --interval=10s --timeout=5s --retries=12 --start-period=60s \
|
| 54 |
+
CMD curl -f http://localhost:${PORT:-8000}/health || exit 1
|
| 55 |
|
| 56 |
+
# Shell form so ${PORT} is expanded at runtime (Render injects PORT; local defaults to 8000).
|
| 57 |
+
CMD uv run uvicorn src.api.main:app --host 0.0.0.0 --port ${PORT:-8000}
|
Procfile
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
web: uv run uvicorn src.api.main:app --host 0.0.0.0 --port $PORT
|
docs/DEPLOY.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deploy β Render (free tier)
|
| 2 |
+
|
| 3 |
+
This guide walks through deploying `youtube_hate_detector` to [Render](https://render.com)
|
| 4 |
+
using the Blueprint (`render.yaml`) at the root of the repo. The Blueprint provisions
|
| 5 |
+
two services: a Dockerised FastAPI backend (`signalmod-api`) and a static React/Vite
|
| 6 |
+
frontend (`signalmod-ui`).
|
| 7 |
+
|
| 8 |
+
## Prerequisites
|
| 9 |
+
|
| 10 |
+
- A free [Render](https://dashboard.render.com/register) account.
|
| 11 |
+
- This repository pushed to GitHub (Render reads the Blueprint from the connected branch).
|
| 12 |
+
- Credentials for any external services you plan to use:
|
| 13 |
+
- `YOUTUBE_API_KEY` β Google Cloud Console β APIs & Services β Credentials.
|
| 14 |
+
- `SUPABASE_URL` and `SUPABASE_KEY` β Supabase project settings (optional, only if
|
| 15 |
+
persistence is enabled).
|
| 16 |
+
|
| 17 |
+
## One-shot deploy (Blueprint)
|
| 18 |
+
|
| 19 |
+
1. Open the Render dashboard β **New** β **Blueprint**.
|
| 20 |
+
2. Connect the GitHub account and pick this repository / branch.
|
| 21 |
+
3. Render parses `render.yaml` and shows the two services. Click **Apply**.
|
| 22 |
+
4. Wait for the first build. The API image takes ~5β8 minutes (frontend build +
|
| 23 |
+
Python deps + spaCy/NLTK data).
|
| 24 |
+
5. Once the API is live, open **signalmod-api β Environment** and fill in the
|
| 25 |
+
secrets that were declared with `sync: false`:
|
| 26 |
+
- `YOUTUBE_API_KEY`
|
| 27 |
+
- `SUPABASE_URL`
|
| 28 |
+
- `SUPABASE_KEY`
|
| 29 |
+
6. Trigger a manual redeploy so the API picks up the new env vars.
|
| 30 |
+
|
| 31 |
+
The static site (`signalmod-ui`) reads `VITE_API_BASE_URL` at build time and points
|
| 32 |
+
at `https://signalmod-api.onrender.com`. If you renamed the API service, update that
|
| 33 |
+
env var in `render.yaml` and re-apply the Blueprint.
|
| 34 |
+
|
| 35 |
+
## Important free-tier caveats
|
| 36 |
+
|
| 37 |
+
- **Cold starts.** Free web services sleep after 15 minutes of inactivity. The first
|
| 38 |
+
request after a sleep can take 30β60 seconds while the container restarts and the
|
| 39 |
+
model warms up. Subsequent requests are fast.
|
| 40 |
+
- **Memory ceiling: 512 MB.** The default production model
|
| 41 |
+
(`Meta-Feature Stacking (Production)`) loads BERT and needs roughly 700 MB of RAM,
|
| 42 |
+
so it will OOM on free. The Blueprint pins `MODEL_NAME="LR + TF-IDF (Baseline)"`,
|
| 43 |
+
which runs comfortably under the limit. If you upgrade to a paid plan with
|
| 44 |
+
β₯1 GB RAM, you can switch the env var to the meta-stacking model and rebuild the
|
| 45 |
+
image with `INSTALL_HF=1`.
|
| 46 |
+
- **Build image size.** `render.yaml` builds with the default `INSTALL_HF=0` so the
|
| 47 |
+
Docker image stays small (no torch/transformers). Locally, `docker-compose.yml`
|
| 48 |
+
still passes `INSTALL_HF=1` for parity with the production model.
|
| 49 |
+
|
| 50 |
+
## Test the Docker image locally before pushing
|
| 51 |
+
|
| 52 |
+
```bash
|
| 53 |
+
# Build the same image Render will build (free-tier shape β no HF extras)
|
| 54 |
+
docker build --build-arg INSTALL_HF=0 -t signalmod-api:render .
|
| 55 |
+
|
| 56 |
+
# Run it with the same model the Blueprint will use
|
| 57 |
+
docker run --rm -p 8000:8000 \
|
| 58 |
+
-e MODEL_NAME="LR + TF-IDF (Baseline)" \
|
| 59 |
+
-e ENV=production \
|
| 60 |
+
signalmod-api:render
|
| 61 |
+
|
| 62 |
+
# Health check
|
| 63 |
+
curl http://localhost:8000/health
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
If `/health` returns `200 OK` locally, the same image will boot on Render.
|
render.yaml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Render Blueprint β youtube_hate_detector
|
| 2 |
+
# Deploy: New + Blueprint β connect repo β Apply.
|
| 3 |
+
# Secrets (sync: false) must be filled in the Render dashboard after the first apply.
|
| 4 |
+
# Free tier: 512 MB RAM β the Meta-Feature Stacking (Production) model does NOT fit.
|
| 5 |
+
# Keep MODEL_NAME at "LR + TF-IDF (Baseline)" for free deploys.
|
| 6 |
+
|
| 7 |
+
services:
|
| 8 |
+
- type: web
|
| 9 |
+
name: signalmod-api
|
| 10 |
+
env: docker
|
| 11 |
+
plan: free
|
| 12 |
+
region: frankfurt
|
| 13 |
+
dockerfilePath: ./Dockerfile
|
| 14 |
+
healthCheckPath: /health
|
| 15 |
+
envVars:
|
| 16 |
+
- key: MODEL_NAME
|
| 17 |
+
value: "LR + TF-IDF (Baseline)"
|
| 18 |
+
- key: ENV
|
| 19 |
+
value: production
|
| 20 |
+
- key: YOUTUBE_API_KEY
|
| 21 |
+
sync: false
|
| 22 |
+
- key: SUPABASE_URL
|
| 23 |
+
sync: false
|
| 24 |
+
- key: SUPABASE_KEY
|
| 25 |
+
sync: false
|
| 26 |
+
|
| 27 |
+
- type: static
|
| 28 |
+
name: signalmod-ui
|
| 29 |
+
env: static
|
| 30 |
+
buildCommand: cd frontend && npm install && npm run build
|
| 31 |
+
staticPublishPath: ./frontend/dist
|
| 32 |
+
envVars:
|
| 33 |
+
- key: VITE_API_BASE_URL
|
| 34 |
+
value: https://signalmod-api.onrender.com
|
| 35 |
+
routes:
|
| 36 |
+
- type: rewrite
|
| 37 |
+
source: /*
|
| 38 |
+
destination: /index.html
|