# Render Configuration Guide Common configuration patterns, best practices, and troubleshooting for Render deployments. ## Environment Variables ### Required vs Optional Variables **Always declare ALL environment variables in render.yaml**, even if values are provided by user later. **Three categories:** 1. **Configuration values** (hardcoded): ```yaml envVars: - key: NODE_ENV value: production - key: LOG_LEVEL value: info - key: API_URL value: https://api.example.com ``` 2. **Secrets** (user provides): ```yaml envVars: - key: JWT_SECRET sync: false - key: STRIPE_SECRET_KEY sync: false - key: API_KEY sync: false ``` 3. **Auto-generated** (Render provides): ```yaml envVars: - key: SESSION_SECRET generateValue: true - key: ENCRYPTION_KEY generateValue: true ``` ### Database Connection Patterns **PostgreSQL:** ```yaml envVars: - key: DATABASE_URL fromDatabase: name: postgres property: connectionString ``` **Redis:** ```yaml envVars: - key: REDIS_URL fromDatabase: name: redis property: connectionString ``` **Multiple databases:** ```yaml envVars: - key: PRIMARY_DB_URL fromDatabase: name: postgres-primary property: connectionString - key: ANALYTICS_DB_URL fromDatabase: name: postgres-analytics property: connectionString - key: CACHE_URL fromDatabase: name: redis property: connectionString ``` ### Cross-Service References Reference other services in your account: ```yaml services: - type: web name: frontend runtime: node envVars: - key: API_URL fromService: name: backend-api type: web property: host # or hostport, port - type: web name: backend-api runtime: node ``` **Available properties:** - `host`: Service hostname - `port`: Service port - `hostport`: Combined `host:port` ### Environment Variable Groups Share common configuration across services: ```yaml envVarGroups: - name: common-config envVars: - key: NODE_ENV value: production - key: LOG_LEVEL value: info - key: TZ value: UTC services: - type: web name: web-app runtime: node envVars: - fromGroup: common-config - key: PORT value: 10000 - type: worker name: worker runtime: node envVars: - fromGroup: common-config ``` --- ## Port Binding ### The Port Binding Requirement **CRITICAL:** Web services must bind to `0.0.0.0:$PORT` **Why this matters:** - Render sets `PORT` environment variable (default: 10000) - Services must bind to `0.0.0.0` (not `localhost` or `127.0.0.1`) - Health checks fail if port binding is incorrect - Deployment will fail or service won't receive traffic ### Code Examples by Language **Node.js / Express:** ```javascript const express = require('express'); const app = express(); const PORT = process.env.PORT || 3000; app.listen(PORT, '0.0.0.0', () => { console.log(`Server running on port ${PORT}`); }); ``` **Python / Flask:** ```python import os from flask import Flask app = Flask(__name__) if __name__ == '__main__': port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port) ``` **Python / Django:** In `settings.py`: ```python # Django runs on port specified by environment ALLOWED_HOSTS = ['*'] ``` Start command in render.yaml: ```yaml startCommand: gunicorn config.wsgi:application --bind 0.0.0.0:$PORT ``` **Python / FastAPI:** ```python import os import uvicorn from fastapi import FastAPI app = FastAPI() if __name__ == "__main__": port = int(os.environ.get("PORT", 8000)) uvicorn.run(app, host="0.0.0.0", port=port) ``` Start command: ```yaml startCommand: uvicorn main:app --host 0.0.0.0 --port $PORT ``` **Go:** ```go package main import ( "fmt" "net/http" "os" ) func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } http.HandleFunc("/", handler) fmt.Printf("Server starting on port %s\n", port) http.ListenAndServe(":"+port, nil) } ``` **Ruby / Rails:** In `config/puma.rb`: ```ruby port ENV.fetch("PORT") { 3000 } bind "tcp://0.0.0.0:#{ENV.fetch('PORT', 3000)}" ``` **Rust / Actix:** ```rust use actix_web::{App, HttpServer}; use std::env; #[actix_web::main] async fn main() -> std::io::Result<()> { let port = env::var("PORT").unwrap_or_else(|_| "8080".to_string()); let addr = format!("0.0.0.0:{}", port); HttpServer::new(|| App::new()) .bind(&addr)? .run() .await } ``` --- ## Build Commands ### Non-Interactive Flags **Always use non-interactive flags** to prevent builds from hanging waiting for input. **npm (Node.js):** ```yaml buildCommand: npm ci # NOT: npm install ``` **pip (Python):** ```yaml buildCommand: pip install -r requirements.txt # Already non-interactive ``` **apt (System packages):** ```yaml buildCommand: apt-get update && apt-get install -y libpq-dev # Use -y flag to auto-confirm ``` **bundler (Ruby):** ```yaml buildCommand: bundle install --jobs=4 --retry=3 ``` ### Build with Additional Steps **Node.js with build step:** ```yaml buildCommand: npm ci && npm run build ``` **Python Django with static files:** ```yaml buildCommand: pip install -r requirements.txt && python manage.py collectstatic --no-input ``` **Ruby Rails with assets:** ```yaml buildCommand: bundle install && bundle exec rails assets:precompile ``` ### Build Timeouts **Free tier:** 15 minutes **Paid tiers:** Configurable **If builds timeout:** 1. Optimize dependencies (remove unused packages) 2. Use build caching 3. Consider pre-building in CI/CD 4. Upgrade to paid tier for longer timeouts --- ## Database Connections ### Internal vs External URLs **Use internal URLs for better performance:** When using `fromDatabase`, Render automatically provides internal `.render-internal.com` URLs: ```yaml envVars: - key: DATABASE_URL fromDatabase: name: postgres property: connectionString ``` This provides: `postgresql://user:pass@postgres.render-internal.com:5432/db` **Benefits:** - Lower latency (same data center) - No external bandwidth charges - Automatic internal DNS ### Connection Pooling **Node.js / PostgreSQL:** ```javascript const { Pool } = require('pg'); const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, max: 20, // Maximum pool size idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); ``` **Python / PostgreSQL:** ```python import psycopg2.pool pool = psycopg2.pool.SimpleConnectionPool( minconn=1, maxconn=20, dsn=os.environ['DATABASE_URL'] ) ``` **Django Settings:** ```python DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'URL': os.environ['DATABASE_URL'], 'CONN_MAX_AGE': 600, # Connection pooling } } ``` ### Database Migrations **Run migrations during build:** **Django:** ```yaml buildCommand: pip install -r requirements.txt && python manage.py migrate ``` **Rails:** ```yaml buildCommand: bundle install && bundle exec rails db:migrate ``` **Node.js / Prisma:** ```yaml buildCommand: npm ci && npx prisma migrate deploy ``` --- ## Free Tier Limitations ### What's Included **Free tier provides:** - 1 web service - 1 PostgreSQL database (1 GB storage, 97 MB RAM) - 750 hours/month compute - 512 MB RAM per service - 0.5 CPU per service - 100 GB bandwidth/month ### Resource Limits **Memory (512 MB):** - Monitor memory usage in logs - Optimize for memory-constrained environments - Use lightweight dependencies **CPU (0.5 cores):** - Suitable for low-traffic applications - Consider upgrading for higher traffic **Spin Down (Free services):** - Services spin down after 15 minutes of inactivity - First request after spin down takes ~30 seconds (cold start) - Upgrade to paid tier for always-on services ### When to Upgrade **Upgrade to paid plan when:** - Need more than 1 web service - Need always-on services (no spin down) - Traffic exceeds free tier limits - Need more memory/CPU - Need faster build times - Need preview environments --- ## Health Checks ### Adding Health Check Endpoints **Node.js / Express:** ```javascript app.get('/health', (req, res) => { res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() }); }); ``` **Python / Flask:** ```python @app.route('/health') def health(): return {'status': 'ok'}, 200 ``` **Python / FastAPI:** ```python @app.get("/health") async def health(): return {"status": "ok"} ``` **Go:** ```go http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status":"ok"}`)) }) ``` ### Configure in render.yaml ```yaml services: - type: web name: my-app runtime: node healthCheckPath: /health ``` **Benefits:** - Faster deployment detection - Better monitoring - Automatic restart on health check failures --- ## Common Deployment Issues ### Issue 1: Missing Environment Variables **Symptom:** Service crashes with "undefined variable" errors **Solution:** Add all required env vars to render.yaml: ```yaml envVars: - key: DATABASE_URL fromDatabase: name: postgres property: connectionString - key: JWT_SECRET sync: false # User fills in Dashboard ``` ### Issue 2: Port Binding Errors **Symptom:** `EADDRINUSE` or health check timeout errors **Solution:** Ensure app binds to `0.0.0.0:$PORT`: ```javascript const PORT = process.env.PORT || 3000; app.listen(PORT, '0.0.0.0'); ``` ### Issue 3: Build Hangs **Symptom:** Build times out after 15 minutes **Solution:** Use non-interactive build commands: ```yaml buildCommand: npm ci # NOT npm install ``` ### Issue 4: Database Connection Fails **Symptom:** `ECONNREFUSED` on port 5432 **Solutions:** 1. Use `fromDatabase` for automatic internal URLs 2. Enable SSL for external connections 3. Check `ipAllowList` settings ### Issue 5: Static Site 404s **Symptom:** Client-side routes return 404 **Solution:** Add SPA rewrite rules: ```yaml routes: - type: rewrite source: /* destination: /index.html ``` ### Issue 6: Out of Memory (OOM) **Symptom:** Service crashes with `JavaScript heap out of memory` **Solutions:** 1. Optimize application memory usage 2. Reduce dependency size 3. Upgrade to higher plan with more RAM --- ## Best Practices Checklist **Environment Variables:** - [ ] All env vars declared in render.yaml - [ ] Secrets marked with `sync: false` - [ ] Database URLs use `fromDatabase` references **Port Binding:** - [ ] App binds to `process.env.PORT` - [ ] Bind to `0.0.0.0` (not `localhost`) **Build Commands:** - [ ] Use non-interactive flags (`npm ci`, `-y`, etc.) - [ ] Build completes under 15 minutes (free tier) **Start Commands:** - [ ] Command starts HTTP server correctly - [ ] Server binds to correct port **Health Checks:** - [ ] `/health` endpoint implemented - [ ] Returns 200 status code **Database:** - [ ] Connection pooling configured - [ ] Using internal URLs (`.render-internal.com`) - [ ] SSL enabled if needed **Plans:** - [ ] Using `plan: free` by default - [ ] Documented upgrade path for users **Git Repository:** - [ ] render.yaml committed to repository - [ ] Pushed to git remote (GitHub/GitLab/Bitbucket) - [ ] Branch specified in render.yaml (if not main) --- ## Additional Resources - Blueprint Specification: [blueprint-spec.md](blueprint-spec.md) - Service Types: [service-types.md](service-types.md) - Runtimes: [runtimes.md](runtimes.md) - Official Render Docs: https://render.com/docs