diff --git a/backend/Procfile b/backend/Procfile new file mode 100644 index 00000000..3f32b904 --- /dev/null +++ b/backend/Procfile @@ -0,0 +1 @@ +web: gunicorn -c gunicorn_config.py app:app diff --git a/backend/gunicorn_config.py b/backend/gunicorn_config.py new file mode 100644 index 00000000..08291d2e --- /dev/null +++ b/backend/gunicorn_config.py @@ -0,0 +1,44 @@ +"""Gunicorn configuration for Render deployment.""" +import os +import multiprocessing + +# Bind to the port Render provides +bind = f"0.0.0.0:{os.getenv('PORT', '10000')}" + +# Use uvicorn workers +worker_class = "uvicorn.workers.UvicornWorker" + +# Single worker for memory efficiency on free tier +workers = 1 + +# Timeout for slow startup (10 minutes to allow for model loading) +timeout = 600 + +# Graceful timeout +graceful_timeout = 30 + +# Keepalive +keepalive = 5 + +# Logging +accesslog = "-" +errorlog = "-" +loglevel = os.getenv("LOG_LEVEL", "info").lower() + +# Preload app (set to False for faster startup if needed) +preload_app = True + + +def on_starting(server): + """Called just before the master process is initialized.""" + print(f"[GUNICORN] Starting on {bind}", flush=True) + + +def on_reload(server): + """Called when worker is reloaded.""" + print(f"[GUNICORN] Reloading workers", flush=True) + + +def when_ready(server): + """Called just after the server is started.""" + print(f"[GUNICORN] Server is ready. Accepting connections.", flush=True) diff --git a/backend/render-build.sh b/backend/render-build.sh index 4fdcb455..5ec39d5f 100644 --- a/backend/render-build.sh +++ b/backend/render-build.sh @@ -2,8 +2,22 @@ set -euo pipefail python -m pip install --upgrade pip setuptools wheel + +# Check if podcast-only mode is enabled - skip heavy ML models if so +ENABLED_FEATURES="${ALWRITY_ENABLED_FEATURES:-all}" +IS_PODCAST_ONLY=false + +if [[ "$ENABLED_FEATURES" == "podcast" ]]; then + IS_PODCAST_ONLY=true + echo "🔊 Podcast-only mode detected - skipping spaCy/NLTK model download" +fi + python -m pip install --retries 10 --timeout 120 -r requirements.txt -# Download required NLTK and spaCy models during build phase -python -m spacy download en_core_web_sm -python -m nltk.downloader punkt_tab stopwords averaged_perceptron_tagger +# Download required NLTK and spaCy models during build phase (skip for podcast-only) +if [[ "$IS_PODCAST_ONLY" == "false" ]]; then + python -m spacy download en_core_web_sm + python -m nltk.downloader punkt_tab stopwords averaged_perceptron_tagger +else + echo "🔊 Skipping spaCy/NLTK download for podcast-only mode" +fi diff --git a/backend/requirements.txt b/backend/requirements.txt index 9d695301..6331d35d 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -3,6 +3,8 @@ fastapi>=0.115.14 starlette>=0.40.0,<0.47.0 sse-starlette<3.0.0 uvicorn>=0.24.0 +uvicorn[standard]>=0.24.0 +gunicorn>=21.0.0 python-multipart>=0.0.6 python-dotenv>=1.0.0 loguru>=0.7.2