Ruperth commited on
Commit
f075eda
Β·
1 Parent(s): c2dca51

feat: prepare project for render free tier deployment

Browse files
Files changed (4) hide show
  1. Dockerfile +4 -2
  2. Procfile +1 -0
  3. docs/DEPLOY.md +66 -0
  4. 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
- CMD ["uv", "run", "uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "8000"]
 
 
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